Compare commits

..

12 Commits

Author SHA1 Message Date
dependabot[bot]
b429fd9c81 Bump cryptography from 46.0.5 to 46.0.6
Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.5 to 46.0.6.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/46.0.5...46.0.6)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 46.0.6
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-01 16:42:49 +00:00
Donncha Ó Cearbhaill
ccd563f2ba Improve docker images tags based on PR #740 (#754)
* Modify docker image deployment behavior

* Use build-push-action to create image provenance & sbom attestations

* Upgrade github actions

* Fix inconsistent capitialization in dockerfiles

---------

Co-authored-by: scribblemaniac <scribblemaniac@users.noreply.github.com>
2026-04-01 18:42:30 +02:00
dependabot[bot]
c681d264b3 Bump tzdata from 2025.2 to 2025.3 (#729)
Bumps [tzdata](https://github.com/python/tzdata) from 2025.2 to 2025.3.
- [Release notes](https://github.com/python/tzdata/releases)
- [Changelog](https://github.com/python/tzdata/blob/master/NEWS.md)
- [Commits](https://github.com/python/tzdata/compare/2025.2...2025.3)

---
updated-dependencies:
- dependency-name: tzdata
  dependency-version: '2025.3'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: besendorf <janik@besendorf.org>
2026-04-01 10:57:30 +02:00
dependabot[bot]
261b0ae000 Bump pydantic-settings from 2.10.1 to 2.13.1 (#750)
Bumps [pydantic-settings](https://github.com/pydantic/pydantic-settings) from 2.10.1 to 2.13.1.
- [Release notes](https://github.com/pydantic/pydantic-settings/releases)
- [Commits](https://github.com/pydantic/pydantic-settings/compare/2.10.1...v2.13.1)

---
updated-dependencies:
- dependency-name: pydantic-settings
  dependency-version: 2.13.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: besendorf <janik@besendorf.org>
2026-04-01 10:54:19 +02:00
dependabot[bot]
89d30e84f4 Bump packaging from 25.0 to 26.0 (#751)
Bumps [packaging](https://github.com/pypa/packaging) from 25.0 to 26.0.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/25.0...26.0)

---
updated-dependencies:
- dependency-name: packaging
  dependency-version: '26.0'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: besendorf <janik@besendorf.org>
2026-04-01 10:51:50 +02:00
dependabot[bot]
557d0a0cd6 Bump requests from 2.32.5 to 2.33.1 (#752)
Bumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.33.1.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.33.1)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.33.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-01 10:48:45 +02:00
github-actions[bot]
134bfce90f Add new iOS versions and build numbers (#743)
Co-authored-by: DonnchaC <DonnchaC@users.noreply.github.com>
2026-03-26 12:16:59 -04:00
Tek
0141da4293 Fixes bug in IOC import (#749) 2026-03-25 23:23:08 +01:00
dependabot[bot]
5cba61b180 Bump mkdocstrings from 0.30.1 to 1.0.0 (#730)
Bumps [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings) from 0.30.1 to 1.0.0.
- [Release notes](https://github.com/mkdocstrings/mkdocstrings/releases)
- [Changelog](https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.1...1.0.0)

---
updated-dependencies:
- dependency-name: mkdocstrings
  dependency-version: 1.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: besendorf <janik@besendorf.org>
2026-03-25 15:24:07 +01:00
dependabot[bot]
29475acb47 Bump click from 8.3.0 to 8.3.1 (#731)
Bumps [click](https://github.com/pallets/click) from 8.3.0 to 8.3.1.
- [Release notes](https://github.com/pallets/click/releases)
- [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/click/compare/8.3.0...8.3.1)

---
updated-dependencies:
- dependency-name: click
  dependency-version: 8.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: besendorf <janik@besendorf.org>
2026-03-25 14:37:59 +01:00
dependabot[bot]
1d5c83582c Bump pydantic from 2.12.3 to 2.12.5 (#732)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.3 to 2.12.5.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.12.3...v2.12.5)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.12.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: besendorf <janik@besendorf.org>
2026-03-25 14:26:09 +01:00
dependabot[bot]
2dd1428787 Bump cryptography from 46.0.3 to 46.0.5 (#747)
Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.3 to 46.0.5.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/46.0.3...46.0.5)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 46.0.5
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-25 08:49:46 +01:00
29 changed files with 196 additions and 143 deletions

View File

@@ -11,7 +11,7 @@ jobs:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v0.5.0
- uses: actions/add-to-project@v1
with:
# You can target a project in a different organization
# to the issue

View File

@@ -7,14 +7,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: 3.9
cache: 'pip'
- name: Checkout
uses: actions/checkout@master
- name: Install Dependencies
run: |
pip install mypy

View File

@@ -4,6 +4,8 @@ name: Create and publish a Docker image
# Configures this workflow to run every time a release is published.
on:
workflow_dispatch:
push:
branches: [main]
release:
types: [published]
@@ -23,9 +25,18 @@ jobs:
attestations: write
id-token: write
#
strategy:
matrix:
platform:
- dockerfile: "Dockerfile"
tag-suffix: ""
- dockerfile: "Dockerfile.ios"
tag-suffix: "-ios"
- dockerfile: "Dockerfile.android"
tag-suffix: "-android"
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
@@ -36,26 +47,33 @@ jobs:
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
flavor: |
latest=false
tags: |
type=raw,value=latest,enable={{ is_default_branch }},suffix=${{ matrix.platform.tag-suffix }}
type=raw,enable=${{ github.event_name == 'release' || github.ref_type == 'tag' }},value=stable,suffix=${{ matrix.platform.tag-suffix }}
type=raw,enable=${{ github.event_name == 'release' }},value=${{ github.event.release.tag_name }},suffix=${{ matrix.platform.tag-suffix }}
type=raw,enable=${{ github.ref_type == 'tag' }},value=${{ github.ref_name }},suffix=${{ matrix.platform.tag-suffix }}
type=sha,suffix=${{ matrix.platform.tag-suffix }}
type=sha,format=long,suffix=${{ matrix.platform.tag-suffix }}
# This step sets up some additional capabilities to generate the provenance and sbom attestations
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
id: push
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
uses: docker/build-push-action@v6
with:
file: ${{ matrix.platform.dockerfile }}
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see "[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)."
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
provenance: mode=max
sbom: true

View File

@@ -11,14 +11,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: 3.9
cache: 'pip'
- name: Checkout
uses: actions/checkout@master
- name: Install Dependencies
run: |
pip install ruff

View File

@@ -15,9 +15,9 @@ jobs:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install Python dependencies

View File

@@ -16,7 +16,7 @@ jobs:
- name: Run script to fetch latest iOS releases from Apple RSS feed.
run: python3 .github/workflows/scripts/update-ios-releases.py
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
uses: peter-evans/create-pull-request@v8
with:
title: '[auto] Update iOS releases and versions'
commit-message: Add new iOS versions and build numbers
@@ -27,4 +27,4 @@ jobs:
add-paths: |
*.json
labels: |
automated pr
automated pr

View File

@@ -1,6 +1,6 @@
# Base image for building libraries
# ---------------------------------
FROM ubuntu:22.04 as build-base
FROM ubuntu:22.04 AS build-base
ARG DEBIAN_FRONTEND=noninteractive
@@ -22,7 +22,7 @@ RUN apt-get update \
# libplist
# --------
FROM build-base as build-libplist
FROM build-base AS build-libplist
# Build
RUN git clone https://github.com/libimobiledevice/libplist && cd libplist \
@@ -32,7 +32,7 @@ RUN git clone https://github.com/libimobiledevice/libplist && cd libplist \
# libimobiledevice-glue
# ---------------------
FROM build-base as build-libimobiledevice-glue
FROM build-base AS build-libimobiledevice-glue
# Install dependencies
COPY --from=build-libplist /build /
@@ -45,7 +45,7 @@ RUN git clone https://github.com/libimobiledevice/libimobiledevice-glue && cd li
# libtatsu
# --------
FROM build-base as build-libtatsu
FROM build-base AS build-libtatsu
# Install dependencies
COPY --from=build-libplist /build /
@@ -58,7 +58,7 @@ RUN git clone https://github.com/libimobiledevice/libtatsu && cd libtatsu \
# libusbmuxd
# ----------
FROM build-base as build-libusbmuxd
FROM build-base AS build-libusbmuxd
# Install dependencies
COPY --from=build-libplist /build /
@@ -72,7 +72,7 @@ RUN git clone https://github.com/libimobiledevice/libusbmuxd && cd libusbmuxd \
# libimobiledevice
# ----------------
FROM build-base as build-libimobiledevice
FROM build-base AS build-libimobiledevice
# Install dependencies
COPY --from=build-libplist /build /
@@ -88,7 +88,7 @@ RUN git clone https://github.com/libimobiledevice/libimobiledevice && cd libimob
# usbmuxd
# -------
FROM build-base as build-usbmuxd
FROM build-base AS build-usbmuxd
# Install dependencies
COPY --from=build-libplist /build /
@@ -103,7 +103,7 @@ RUN git clone https://github.com/libimobiledevice/usbmuxd && cd usbmuxd \
# Create main image
FROM ubuntu:24.04 as main
FROM ubuntu:24.04 AS main
LABEL org.opencontainers.image.url="https://mvt.re"
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"

View File

@@ -1,5 +1,5 @@
# Create main image
FROM python:3.10.14-alpine3.20 as main
FROM python:3.10.14-alpine3.20 AS main
LABEL org.opencontainers.image.url="https://mvt.re"
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"

View File

@@ -1,6 +1,6 @@
# Base image for building libraries
# ---------------------------------
FROM ubuntu:22.04 as build-base
FROM ubuntu:22.04 AS build-base
ARG DEBIAN_FRONTEND=noninteractive
@@ -22,7 +22,7 @@ RUN apt-get update \
# libplist
# --------
FROM build-base as build-libplist
FROM build-base AS build-libplist
# Build
RUN git clone https://github.com/libimobiledevice/libplist && cd libplist \
@@ -32,7 +32,7 @@ RUN git clone https://github.com/libimobiledevice/libplist && cd libplist \
# libimobiledevice-glue
# ---------------------
FROM build-base as build-libimobiledevice-glue
FROM build-base AS build-libimobiledevice-glue
# Install dependencies
COPY --from=build-libplist /build /
@@ -45,7 +45,7 @@ RUN git clone https://github.com/libimobiledevice/libimobiledevice-glue && cd li
# libtatsu
# --------
FROM build-base as build-libtatsu
FROM build-base AS build-libtatsu
# Install dependencies
COPY --from=build-libplist /build /
@@ -58,7 +58,7 @@ RUN git clone https://github.com/libimobiledevice/libtatsu && cd libtatsu \
# libusbmuxd
# ----------
FROM build-base as build-libusbmuxd
FROM build-base AS build-libusbmuxd
# Install dependencies
COPY --from=build-libplist /build /
@@ -72,7 +72,7 @@ RUN git clone https://github.com/libimobiledevice/libusbmuxd && cd libusbmuxd \
# libimobiledevice
# ----------------
FROM build-base as build-libimobiledevice
FROM build-base AS build-libimobiledevice
# Install dependencies
COPY --from=build-libplist /build /
@@ -88,7 +88,7 @@ RUN git clone https://github.com/libimobiledevice/libimobiledevice && cd libimob
# usbmuxd
# -------
FROM build-base as build-usbmuxd
FROM build-base AS build-usbmuxd
# Install dependencies
COPY --from=build-libplist /build /
@@ -104,7 +104,7 @@ RUN git clone https://github.com/libimobiledevice/usbmuxd && cd usbmuxd \
# Main image
# ----------
FROM python:3.10.14-alpine3.20 as main
FROM python:3.10.14-alpine3.20 AS main
LABEL org.opencontainers.image.url="https://mvt.re"
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"

View File

@@ -2,4 +2,4 @@ mkdocs==1.6.1
mkdocs-autorefs==1.4.3
mkdocs-material==9.6.20
mkdocs-material-extensions==1.3.1
mkdocstrings==0.30.1
mkdocstrings==1.0.0

View File

@@ -17,25 +17,25 @@ classifiers = [
"Programming Language :: Python",
]
dependencies = [
"click==8.3.0",
"click==8.3.1",
"rich==14.1.0",
"tld==0.13.1",
"requests==2.32.5",
"requests==2.33.1",
"simplejson==3.20.2",
"packaging==25.0",
"packaging==26.0",
"appdirs==1.4.4",
"iOSbackup==0.9.925",
"adb-shell[usb]==0.4.4",
"libusb1==3.3.1",
"cryptography==46.0.3",
"cryptography==46.0.6",
"PyYAML>=6.0.2",
"pyahocorasick==2.2.0",
"betterproto==1.2.5",
"pydantic==2.12.3",
"pydantic-settings==2.10.1",
"pydantic==2.12.5",
"pydantic-settings==2.13.1",
"NSKeyedUnArchiver==1.5.2",
"python-dateutil==2.9.0.post0",
"tzdata==2025.2",
"tzdata==2025.3",
]
requires-python = ">= 3.10"
@@ -81,8 +81,8 @@ addopts = "-ra -q --cov=mvt --cov-report html --junitxml=pytest.xml --cov-report
testpaths = ["tests"]
[tool.ruff]
select = ["C90", "E", "F", "W"] # flake8 default set
ignore = [
lint.select = ["C90", "E", "F", "W"] # flake8 default set
lint.ignore = [
"E501", # don't enforce line length violations
"C901", # complex-structure
@@ -95,10 +95,10 @@ ignore = [
# "E203", # whitespace-before-punctuation
]
[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # unused-import
[tool.ruff.mccabe]
[tool.ruff.lint.mccabe]
max-complexity = 10
[tool.setuptools]

View File

@@ -186,7 +186,7 @@ class DumpsysPackagesArtifact(AndroidArtifact):
package = []
in_package_list = False
for line in content.splitlines():
for line in content.split("\n"):
if line.startswith("Packages:"):
in_package_list = True
continue

View File

@@ -8,7 +8,7 @@ from .artifact import AndroidArtifact
class Processes(AndroidArtifact):
def parse(self, entry: str) -> None:
for line in entry.splitlines()[1:]:
for line in entry.split("\n")[1:]:
proc = line.split()
# Skip empty lines

View File

@@ -193,7 +193,7 @@ class TombstoneCrashArtifact(AndroidArtifact):
# eg. "Process uptime: 40s"
tombstone[destination_key] = int(value_clean.rstrip("s"))
elif destination_key == "command_line":
# Wrap in list for consistency with protobuf format (repeated string).
# XXX: Check if command line should be a single string in a list, or a list of strings.
tombstone[destination_key] = [value_clean]
else:
tombstone[destination_key] = value_clean

View File

@@ -117,6 +117,8 @@ def download_apks(ctx, all_apks, virustotal, output, from_file, serial, verbose)
if from_file:
download = DownloadAPKs.from_json(from_file)
else:
# TODO: Do we actually want to be able to run without storing any
# file?
if not output:
log.critical("You need to specify an output folder with --output!")
ctx.exit(1)

View File

@@ -105,15 +105,15 @@ class AQFFiles(AndroidQFModule):
)
self.detected.append(result)
for hash_key in ("sha256", "sha1", "md5"):
file_hash = result.get(hash_key, "")
if not file_hash:
continue
ioc = self.indicators.check_file_hash(file_hash)
if ioc:
result["matched_indicator"] = ioc
self.detected.append(result)
break
if result.get("sha256", "") == "":
continue
ioc = self.indicators.check_file_hash(result["sha256"])
if ioc:
result["matched_indicator"] = ioc
self.detected.append(result)
# TODO: adds SHA1 and MD5 when available in MVT
def run(self) -> None:
if timezone := self._get_device_timezone():
@@ -128,7 +128,7 @@ class AQFFiles(AndroidQFModule):
data = json.loads(rawdata)
except json.decoder.JSONDecodeError:
data = []
for line in rawdata.splitlines():
for line in rawdata.split("\n"):
if line.strip() == "":
continue
data.append(json.loads(line))
@@ -139,7 +139,7 @@ class AQFFiles(AndroidQFModule):
utc_timestamp = datetime.datetime.fromtimestamp(
file_data[ts], tz=datetime.timezone.utc
)
# Convert the UTC timestamp to local time on Android device's local timezone
# Convert the UTC timestamp to local tiem on Android device's local timezone
local_timestamp = utc_timestamp.astimezone(device_timezone)
# HACK: We only output the UTC timestamp in convert_datetime_to_iso, we

View File

@@ -39,7 +39,7 @@ class AQFSettings(SettingsArtifact, AndroidQFModule):
self.results[namespace] = {}
data = self._get_file_content(setting_file)
for line in data.decode("utf-8").splitlines():
for line in data.decode("utf-8").split("\n"):
line = line.strip()
try:
key, value = line.split("=", 1)

View File

@@ -222,6 +222,7 @@ class Command:
if self.module_name and module.__name__ != self.module_name:
continue
# FIXME: do we need the logger here
module_logger = logging.getLogger(module.__module__)
m = module(

View File

@@ -52,9 +52,7 @@ class Indicators:
if os.path.isfile(path) and path.lower().endswith(".stix2"):
self.parse_stix2(path)
elif os.path.isdir(path):
for file in glob.glob(
os.path.join(path, "**", "*.stix2", recursive=True)
):
for file in glob.glob(os.path.join(path, "**", "*.stix2"), recursive=True):
self.parse_stix2(file)
else:
self.log.error(

View File

@@ -180,8 +180,10 @@ class IndicatorsUpdates:
def _get_remote_file_latest_commit(
self, owner: str, repo: str, branch: str, path: str
) -> int:
# TODO: The branch is currently not taken into consideration.
# How do we specify which branch to look up to the API?
file_commit_url = (
f"https://api.github.com/repos/{owner}/{repo}/commits?path={path}&sha={branch}"
f"https://api.github.com/repos/{owner}/{repo}/commits?path={path}"
)
try:
res = requests.get(file_commit_url, timeout=5)

View File

@@ -119,9 +119,10 @@ def convert_mactime_to_datetime(timestamp: Union[int, float], from_2001: bool =
if from_2001:
timestamp = timestamp + 978307200
# TODO: This is rather ugly. Happens sometimes with invalid timestamps.
try:
return convert_unix_to_utc_datetime(timestamp)
except (OSError, OverflowError, ValueError):
except Exception:
return None

View File

@@ -907,6 +907,10 @@
"version": "15.8.6",
"build": "19H402"
},
{
"version": "15.8.7",
"build": "19H411"
},
{
"build": "20A362",
"version": "16.0"
@@ -1020,6 +1024,10 @@
"version": "16.7.14",
"build": "20H370"
},
{
"version": "16.7.15",
"build": "20H380"
},
{
"version": "17.0",
"build": "21A327"
@@ -1188,6 +1196,10 @@
"version": "18.7.6",
"build": "22H320"
},
{
"version": "18.7.7",
"build": "22H333"
},
{
"version": "26",
"build": "23A341"
@@ -1215,5 +1227,9 @@
{
"version": "26.3.1",
"build": "23D8133"
},
{
"version": "26.4",
"build": "23E246"
}
]

View File

@@ -87,35 +87,6 @@ class ConfigurationProfiles(IOSExtraction):
self.detected.append(result)
continue
@staticmethod
def _b64encode_key(d: dict, key: str) -> None:
if key in d:
d[key] = b64encode(d[key])
@staticmethod
def _b64encode_keys(d: dict, keys: list) -> None:
for key in keys:
if key in d:
d[key] = b64encode(d[key])
def _b64encode_plist_bytes(self, plist: dict) -> None:
"""Encode binary plist values to base64 for JSON serialization."""
if "SignerCerts" in plist:
plist["SignerCerts"] = [b64encode(x) for x in plist["SignerCerts"]]
self._b64encode_keys(plist, ["PushTokenDataSentToServerKey", "LastPushTokenHash"])
if "OTAProfileStub" in plist:
stub = plist["OTAProfileStub"]
if "SignerCerts" in stub:
stub["SignerCerts"] = [b64encode(x) for x in stub["SignerCerts"]]
if "PayloadContent" in stub:
self._b64encode_key(stub["PayloadContent"], "EnrollmentIdentityPersistentID")
if "PayloadContent" in plist:
for entry in plist["PayloadContent"]:
self._b64encode_keys(entry, ["PERSISTENT_REF", "IdentityPersistentRef"])
def run(self) -> None:
for conf_file in self._get_backup_files_from_manifest(
domain=CONF_PROFILES_DOMAIN
@@ -144,7 +115,65 @@ class ConfigurationProfiles(IOSExtraction):
except Exception:
conf_plist = {}
self._b64encode_plist_bytes(conf_plist)
# TODO: Tidy up the following code hell.
if "SignerCerts" in conf_plist:
conf_plist["SignerCerts"] = [
b64encode(x) for x in conf_plist["SignerCerts"]
]
if "OTAProfileStub" in conf_plist:
if "SignerCerts" in conf_plist["OTAProfileStub"]:
conf_plist["OTAProfileStub"]["SignerCerts"] = [
b64encode(x)
for x in conf_plist["OTAProfileStub"]["SignerCerts"]
]
if "PayloadContent" in conf_plist["OTAProfileStub"]:
if (
"EnrollmentIdentityPersistentID"
in conf_plist["OTAProfileStub"]["PayloadContent"]
):
conf_plist["OTAProfileStub"]["PayloadContent"][
"EnrollmentIdentityPersistentID"
] = b64encode(
conf_plist["OTAProfileStub"]["PayloadContent"][
"EnrollmentIdentityPersistentID"
]
)
if "PushTokenDataSentToServerKey" in conf_plist:
conf_plist["PushTokenDataSentToServerKey"] = b64encode(
conf_plist["PushTokenDataSentToServerKey"]
)
if "LastPushTokenHash" in conf_plist:
conf_plist["LastPushTokenHash"] = b64encode(
conf_plist["LastPushTokenHash"]
)
if "PayloadContent" in conf_plist:
for content_entry in range(len(conf_plist["PayloadContent"])):
if "PERSISTENT_REF" in conf_plist["PayloadContent"][content_entry]:
conf_plist["PayloadContent"][content_entry][
"PERSISTENT_REF"
] = b64encode(
conf_plist["PayloadContent"][content_entry][
"PERSISTENT_REF"
]
)
if (
"IdentityPersistentRef"
in conf_plist["PayloadContent"][content_entry]
):
conf_plist["PayloadContent"][content_entry][
"IdentityPersistentRef"
] = b64encode(
conf_plist["PayloadContent"][content_entry][
"IdentityPersistentRef"
]
)
self.results.append(
{

View File

@@ -73,7 +73,7 @@ class ShutdownLog(IOSExtraction):
recent_processes = []
times_delayed = 0
delay = 0.0
for line in content.splitlines():
for line in content.split("\n"):
line = line.strip()
if line.startswith("remaining client pid:"):

View File

@@ -11,6 +11,7 @@ from mvt.common.utils import convert_chrometime_to_datetime, convert_datetime_to
from ..base import IOSExtraction
CHROME_FAVICON_BACKUP_IDS = ["55680ab883d0fdcffd94f959b1632e5fbbb18c5b"]
# TODO: Confirm Chrome database path.
CHROME_FAVICON_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/Library/Application Support/Google/Chrome/Default/Favicons",
]

View File

@@ -13,6 +13,7 @@ from ..base import IOSExtraction
CHROME_HISTORY_BACKUP_IDS = [
"faf971ce92c3ac508c018dce1bef2a8b8e9838f1",
]
# TODO: Confirm Chrome database path.
CHROME_HISTORY_ROOT_PATHS = [
"private/var/mobile/Containers/Data/Application/*/Library/Application Support/Google/Chrome/Default/History", # pylint: disable=line-too-long
]

View File

@@ -79,55 +79,32 @@ class WebkitResourceLoadStatistics(IOSExtraction):
cur = conn.cursor()
try:
# FIXME: table contains extra fields with timestamp here
cur.execute(
"""
SELECT
domainID,
registrableDomain,
lastSeen,
hadUserInteraction,
mostRecentUserInteractionTime,
mostRecentWebPushInteractionTime
hadUserInteraction
from ObservedDomains;
"""
)
has_extra_timestamps = True
except sqlite3.OperationalError:
try:
cur.execute(
"""
SELECT
domainID,
registrableDomain,
lastSeen,
hadUserInteraction
from ObservedDomains;
"""
)
has_extra_timestamps = False
except sqlite3.OperationalError:
return
return
for row in cur:
result = {
"domain_id": row[0],
"registrable_domain": row[1],
"last_seen": row[2],
"had_user_interaction": bool(row[3]),
"last_seen_isodate": convert_unix_to_iso(row[2]),
"domain": domain,
"path": path,
}
if has_extra_timestamps:
result["most_recent_user_interaction_time"] = row[4]
result["most_recent_user_interaction_time_isodate"] = (
convert_unix_to_iso(row[4])
)
result["most_recent_web_push_interaction_time"] = row[5]
result["most_recent_web_push_interaction_time_isodate"] = (
convert_unix_to_iso(row[5])
)
self.results.append(result)
self.results.append(
{
"domain_id": row[0],
"registrable_domain": row[1],
"last_seen": row[2],
"had_user_interaction": bool(row[3]),
"last_seen_isodate": convert_unix_to_iso(row[2]),
"domain": domain,
"path": path,
}
)
if len(self.results) > 0:
self.log.info(

View File

@@ -76,6 +76,12 @@ class WebkitSessionResourceLog(IOSExtraction):
entry["redirect_destination"]
)
# TODO: Currently not used.
# subframe_origins = self._extract_domains(
# entry["subframe_under_origin"])
# subresource_domains = self._extract_domains(
# entry["subresource_under_origin"])
all_origins = set(
[entry["origin"]] + source_domains + destination_domains
)

View File

@@ -311,11 +311,14 @@ class NetBase(IOSExtraction):
self.results = sorted(self.results, key=operator.itemgetter("first_isodate"))
def check_indicators(self) -> None:
# check_manipulated/find_deleted require "live_isodate" and
# "live_proc_id" keys which may be absent in older result formats.
if self.results and "live_isodate" in self.results[0]:
# Check for manipulated process records.
# TODO: Catching KeyError for live_isodate for retro-compatibility.
# This is not very good.
try:
self.check_manipulated()
self.find_deleted()
except KeyError:
pass
if not self.indicators:
return