From 3f3261511a7645f91dc765ea5d13a6c86d81e83c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donncha=20=C3=93=20Cearbhaill?= Date: Thu, 16 Dec 2021 17:57:26 +0100 Subject: [PATCH] Add module to search for known malicious or suspicious configuration profiles --- docs/ios/records.md | 2 + mvt/common/indicators.py | 18 ++++++++ .../modules/backup/configuration_profiles.py | 42 ++++++++++++++++--- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/docs/ios/records.md b/docs/ios/records.md index d027bee..7b02ecd 100644 --- a/docs/ios/records.md +++ b/docs/ios/records.md @@ -80,6 +80,8 @@ If indicators are provided through the command-line, they are checked against th This JSON file is created by mvt-ios' `ConfigurationProfiles` module. The module extracts details about iOS configuration profiles that have been installed on the device. These should include both default iOS as well as third-party profiles. +If indicators are provided through the command-line, they are checked against the configuration profile UUID to identify any known malicious profiles. Any matches are stored in *configuration_profiles_detected.json*. + --- ### `contacts.json` diff --git a/mvt/common/indicators.py b/mvt/common/indicators.py index e88f317..fd2a2f1 100644 --- a/mvt/common/indicators.py +++ b/mvt/common/indicators.py @@ -28,6 +28,7 @@ class Indicators: self.ioc_files = [] self.ioc_files_sha256 = [] self.ioc_app_ids = [] + self.ios_profile_ids = [] self.ioc_count = 0 self._check_env_variable() @@ -88,6 +89,9 @@ class Indicators: elif key == "app:id": self._add_indicator(ioc=value, iocs_list=self.ioc_app_ids) + elif key == "configuration-profile:id": + self._add_indicator(ioc=value, + iocs_list=self.ios_profile_ids) elif key == "file:hashes.sha256": self._add_indicator(ioc=value, iocs_list=self.ioc_files_sha256) @@ -264,3 +268,17 @@ class Indicators: return True return False + + def check_profile(self, profile_uuid) -> bool: + """Check the provided configuration profile UUID against the list of indicators. + + :param profile_uuid: Profile UUID to check against configuration profile indicators + :type profile_uuid: str + :returns: True if the UUID in indicator list, otherwise False + :rtype: bool + + """ + if profile_uuid in self.ios_profile_ids: + return True + + return False \ No newline at end of file diff --git a/mvt/ios/modules/backup/configuration_profiles.py b/mvt/ios/modules/backup/configuration_profiles.py index 231803e..570707e 100644 --- a/mvt/ios/modules/backup/configuration_profiles.py +++ b/mvt/ios/modules/backup/configuration_profiles.py @@ -6,6 +6,7 @@ import plistlib from base64 import b64encode +from mvt.common.utils import convert_timestamp_to_iso from ..base import IOSExtraction @@ -21,6 +22,36 @@ class ConfigurationProfiles(IOSExtraction): output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) + def serialize(self, record): + if not record["install_date"]: + return + return { + "timestamp": record["install_date"], + "module": self.__class__.__name__, + "event": "configuration_profile_install", + "data": f"{record['plist']['PayloadType']} installed: {record['plist']['PayloadUUID']} - {record['plist']['PayloadDisplayName']}: {record['plist']['PayloadDescription']}" + } + + def check_indicators(self): + if not self.indicators: + return + + for result in self.results: + if result["plist"].get("PayloadUUID"): + payload_content = result["plist"]["PayloadContent"][0] + + # Alert on any known malicious configuration profiles in the indicator list. + if self.indicators.check_profile(result["plist"]["PayloadUUID"]): + self.log.warning(f"Found a known malicious configuration profile \"{result['plist']['PayloadDisplayName']}\" with UUID '{result['plist']['PayloadUUID']}'.") + self.detected.append(result) + continue + + # Highlight suspicious configuration profiles which may be used to hide notifications. + if payload_content["PayloadType"] in ["com.apple.notificationsettings"]: + self.log.warning(f"Found a potentially suspicious configuration profile \"{result['plist']['PayloadDisplayName']}\" with payload type '{payload_content['PayloadType']}'.") + self.detected.append(result) + continue + def run(self): for conf_file in self._get_backup_files_from_manifest(domain=CONF_PROFILES_DOMAIN): conf_file_path = self._get_backup_file_from_id(conf_file["file_id"]) @@ -36,19 +67,20 @@ class ConfigurationProfiles(IOSExtraction): if "SignerCerts" in conf_plist: conf_plist["SignerCerts"] = [b64encode(x) for x in conf_plist["SignerCerts"]] if "PushTokenDataSentToServerKey" in conf_plist: - conf_plist["PushTokenDataSentToServerKey"] = b64encode(conf_plist["PushTokenDataSentToServerKey"]) + conf_plist["PushTokenDataSentToServerKey"] = b64encode(conf_plist["PushTokenDataSentToServerKey"]) if "LastPushTokenHash" in conf_plist: - conf_plist["LastPushTokenHash"] = b64encode(conf_plist["LastPushTokenHash"]) + conf_plist["LastPushTokenHash"] = b64encode(conf_plist["LastPushTokenHash"]) if "PayloadContent" in conf_plist: - for x in range(len(conf_plist["PayloadContent"])): - if "PERSISTENT_REF" in conf_plist["PayloadContent"][x]: - conf_plist["PayloadContent"][x]["PERSISTENT_REF"] = b64encode(conf_plist["PayloadContent"][x]["PERSISTENT_REF"]) + for x in range(len(conf_plist["PayloadContent"])): + if "PERSISTENT_REF" in conf_plist["PayloadContent"][x]: + conf_plist["PayloadContent"][x]["PERSISTENT_REF"] = b64encode(conf_plist["PayloadContent"][x]["PERSISTENT_REF"]) self.results.append({ "file_id": conf_file["file_id"], "relative_path": conf_file["relative_path"], "domain": conf_file["domain"], "plist": conf_plist, + "install_date": convert_timestamp_to_iso(conf_plist.get("InstallDate")), }) self.log.info("Extracted details about %d configuration profiles", len(self.results))