mirror of
https://github.com/mvt-project/mvt.git
synced 2026-04-03 18:42:25 +02:00
Compare commits
12 Commits
todos
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b429fd9c81 | ||
|
|
ccd563f2ba | ||
|
|
c681d264b3 | ||
|
|
261b0ae000 | ||
|
|
89d30e84f4 | ||
|
|
557d0a0cd6 | ||
|
|
134bfce90f | ||
|
|
0141da4293 | ||
|
|
5cba61b180 | ||
|
|
29475acb47 | ||
|
|
1d5c83582c | ||
|
|
2dd1428787 |
2
.github/workflows/add-issue-to-project.yml
vendored
2
.github/workflows/add-issue-to-project.yml
vendored
@@ -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
|
||||
|
||||
7
.github/workflows/mypy.yml
vendored
7
.github/workflows/mypy.yml
vendored
@@ -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
|
||||
|
||||
40
.github/workflows/publish-release-docker.yml
vendored
40
.github/workflows/publish-release-docker.yml
vendored
@@ -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
|
||||
|
||||
|
||||
5
.github/workflows/ruff.yml
vendored
5
.github/workflows/ruff.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/update-ios-data.yml
vendored
4
.github/workflows/update-ios-data.yml
vendored
@@ -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
|
||||
|
||||
16
Dockerfile
16
Dockerfile
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
@@ -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(
|
||||
{
|
||||
|
||||
@@ -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:"):
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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
|
||||
]
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user