Added modules to extract details on configuration profiles from backup

This commit is contained in:
Nex
2021-08-15 18:53:02 +02:00
parent 9e5a412fe2
commit 5e6e4fa8d0
7 changed files with 130 additions and 16 deletions

View File

@@ -92,9 +92,9 @@ class MVTModule(object):
if self.results:
results_file_name = f"{name}.json"
results_json_path = os.path.join(self.output_folder, results_file_name)
with open(results_json_path, "w") as handle:
with io.open(results_json_path, "w", encoding="utf-8") as handle:
try:
json.dump(self.results, handle, indent=4)
json.dump(self.results, handle, indent=4, default=str)
except Exception as e:
self.log.error("Unable to store results of module %s to file %s: %s",
self.__class__.__name__, results_file_name, e)
@@ -102,8 +102,8 @@ class MVTModule(object):
if self.detected:
detected_file_name = f"{name}_detected.json"
detected_json_path = os.path.join(self.output_folder, detected_file_name)
with open(detected_json_path, "w") as handle:
json.dump(self.detected, handle, indent=4)
with io.open(detected_json_path, "w", encoding="utf-8") as handle:
json.dump(self.detected, handle, indent=4, default=str)
def serialize(self, record):
raise NotImplementedError
@@ -193,8 +193,8 @@ def save_timeline(timeline, timeline_path):
csvoutput.writerow(["UTC Timestamp", "Plugin", "Event", "Description"])
for event in sorted(timeline, key=lambda x: x["timestamp"] if x["timestamp"] is not None else ""):
csvoutput.writerow([
event["timestamp"],
event["module"],
event["event"],
event["data"],
event.get("timestamp"),
event.get("module"),
event.get("event"),
event.get("data"),
])

View File

@@ -4,6 +4,8 @@
# https://license.mvt.re/1.1/
from .backup_info import BackupInfo
from .configuration_profiles import ConfigurationProfiles
from .manifest import Manifest
from .profile_events import ProfileEvents
BACKUP_MODULES = [BackupInfo, Manifest,]
BACKUP_MODULES = [BackupInfo, ConfigurationProfiles, Manifest, ProfileEvents]

View File

@@ -0,0 +1,43 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021 The MVT Project Authors.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import os
import plistlib
from base64 import b64encode
from ..base import IOSExtraction
CONF_PROFILES_DOMAIN = "SysSharedContainerDomain-systemgroup.com.apple.configurationprofiles"
class ConfigurationProfiles(IOSExtraction):
"""This module extracts the full plist data from configuration profiles.
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
fast_mode=False, log=None, results=[]):
super().__init__(file_path=file_path, base_folder=base_folder,
output_folder=output_folder, fast_mode=fast_mode,
log=log, results=results)
def run(self):
for conf_file in self._get_files_from_manifest(domain=CONF_PROFILES_DOMAIN):
conf_file_path = self._get_backup_file_from_id(conf_file["file_id"])
if not conf_file_path:
continue
with open(conf_file_path, "rb") as handle:
conf_plist = plistlib.load(handle)
if "SignerCerts" in conf_plist:
conf_plist["SignerCerts"] = [b64encode(x) for x in conf_plist["SignerCerts"]]
self.results.append(dict(
file_id=conf_file["file_id"],
relative_path=conf_file["relative_path"],
domain=conf_file["domain"],
plist=conf_plist,
))
self.log.info("Extracted details about %d configuration profiles", len(self.results))

View File

@@ -0,0 +1,59 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021 The MVT Project Authors.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import plistlib
from datetime import datetime
from mvt.common.utils import convert_timestamp_to_iso
from ..base import IOSExtraction
CONF_PROFILES_EVENTS_RELPATH = "Library/ConfigurationProfiles/MCProfileEvents.plist"
class ProfileEvents(IOSExtraction):
"""This module extracts events related to the installation of configuration
profiles.
"""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
fast_mode=False, log=None, results=[]):
super().__init__(file_path=file_path, base_folder=base_folder,
output_folder=output_folder, fast_mode=fast_mode,
log=log, results=results)
def serialize(self, record):
return {
"timestamp": record.get("timestamp"),
"module": self.__class__.__name__,
"event": "profile_operation",
"data": f"Process {record.get('process')} started operation {record.get('operation')} of profile {record.get('profile_id')}"
}
def run(self):
for events_file in self._get_files_from_manifest(relative_path=CONF_PROFILES_EVENTS_RELPATH):
events_file_path = self._get_backup_file_from_id(events_file["file_id"])
if not events_file_path:
continue
with open(events_file_path, "rb") as handle:
events_plist = plistlib.load(handle)
if "ProfileEvents" not in events_plist:
continue
for event in events_plist["ProfileEvents"]:
key = list(event.keys())[0]
self.log.info("On %s process \"%s\" started operation \"%s\" of profile \"%s\"",
event[key].get("timestamp"), event[key].get("process"),
event[key].get("operation"), key)
self.results.append({
"profile_id": key,
"timestamp": convert_timestamp_to_iso(event[key].get("timestamp")),
"operation": event[key].get("operation"),
"process": event[key].get("process"),
})
self.log.info("Extracted %d profile events", len(self.results))

View File

@@ -94,7 +94,18 @@ class IOSExtraction(MVTModule):
raise Exception("Query to Manifest.db failed: %s", e)
for row in cur:
yield dict(file_id=row[0], domain=row[1], relative_path=row[2])
yield {
"file_id": row[0],
"domain": row[1],
"relative_path": row[2],
}
def _get_backup_file_from_id(self, file_id):
file_path = os.path.join(self.base_folder, file_id[0:2], file_id)
if os.path.exists(file_path):
return file_path
return None
def _find_ios_database(self, backup_ids=None, root_paths=[]):
"""Try to locate the module's database file from either an iTunes
@@ -110,9 +121,8 @@ class IOSExtraction(MVTModule):
# folder structure, if we have a valid ID.
if backup_ids:
for backup_id in backup_ids:
file_path = os.path.join(self.base_folder, backup_id[0:2], backup_id)
# If we found the correct backup file, then we stop searching.
if os.path.exists(file_path):
file_path = self._get_backup_file_from_id(backup_id)
if file_path:
break
# If this file does not exist we might be processing a full

View File

@@ -25,7 +25,7 @@ class Filesystem(IOSExtraction):
return {
"timestamp": record["modified"],
"module": self.__class__.__name__,
"event": f"file_modified",
"event": "file_modified",
"data": record["file_path"],
}

View File

@@ -28,6 +28,8 @@ class WebkitResourceLoadStatistics(IOSExtraction):
output_folder=output_folder, fast_mode=fast_mode,
log=log, results=results)
self.results = {}
def check_indicators(self):
if not self.indicators:
return
@@ -72,8 +74,6 @@ class WebkitResourceLoadStatistics(IOSExtraction):
self.log.info("Extracted a total of %d records from %s", len(self.results[key]), db_path)
def run(self):
self.results = {}
if self.is_backup:
try:
for backup_file in self._get_files_from_manifest(relative_path=WEBKIT_RESOURCELOADSTATICS_BACKUP_RELPATH):