Adds calendar iOS plugin

This commit is contained in:
tek 2023-04-12 10:21:17 +02:00
parent b1e5dc715f
commit 33d092692e
8 changed files with 183 additions and 14 deletions

View File

@ -3,10 +3,10 @@
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import copy
import logging
import plistlib
import sqlite3
import copy
from typing import Optional, Union
from mvt.common.utils import convert_mactime_to_iso

View File

@ -15,8 +15,6 @@ from ..base import IOSExtraction
class Filesystem(IOSExtraction):
"""This module extracts creation and modification date of files from a
full file-system dump.
"""
def __init__(

View File

@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
from .applications import Applications
from .calendar import Calendar
from .calls import Calls
from .chrome_favicon import ChromeFavicon
from .chrome_history import ChromeHistory
@ -29,4 +30,5 @@ MIXED_MODULES = [Calls, ChromeFavicon, ChromeHistory, Contacts, FirefoxFavicon,
FirefoxHistory, IDStatusCache, InteractionC, LocationdClients,
OSAnalyticsADDaily, Datausage, SafariBrowserState, SafariHistory,
TCC, SMS, SMSAttachments, WebkitResourceLoadStatistics,
WebkitSessionResourceLog, Whatsapp, Shortcuts, Applications]
WebkitSessionResourceLog, Whatsapp, Shortcuts, Applications,
Calendar]

View File

@ -3,16 +3,15 @@
# 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 logging
import plistlib
import hashlib
import logging
import os
import plistlib
from datetime import datetime, timezone
from typing import Optional, Union, Dict, Any
from typing import Any, Dict, Optional, Union
from mvt.common.utils import convert_datetime_to_iso
from mvt.common.module import DatabaseNotFoundError
from mvt.common.utils import convert_datetime_to_iso
from mvt.ios.modules.base import IOSExtraction
APPLICATIONS_DB_PATH = [

View File

@ -0,0 +1,136 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2023 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
import sqlite3
from typing import Optional, Union
from mvt.common.utils import convert_mactime_to_iso
from ..base import IOSExtraction
CALENDAR_BACKUP_IDS = [
"2041457d5fe04d39d0ab481178355df6781e6858",
]
CALENDAR_ROOT_PATHS = [
"private/var/mobile/Library/Calendar/Calendar.sqlitedb"
]
class Calendar(IOSExtraction):
"""This module extracts all calendar entries."""
def __init__(
self,
file_path: Optional[str] = None,
target_path: Optional[str] = None,
results_path: Optional[str] = None,
fast_mode: Optional[bool] = False,
log: logging.Logger = logging.getLogger(__name__),
results: Optional[list] = None
) -> None:
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.timestamps = [
"start_date",
"end_date",
"last_modified",
"creation_date",
"participant_last_modified"
]
def serialize(self, record: dict) -> Union[dict, list]:
records = []
for timestamp in self.timestamps:
if timestamp not in record or not record[timestamp]:
continue
records.append({
"timestamp": record[timestamp],
"module": self.__class__.__name__,
"event": timestamp,
"data": f"Calendar event {record['summary']} ({record['description']}) "
f"(invitation by {record['participant_email']})"
})
return records
def check_indicators(self) -> None:
for result in self.results:
if result["participant_email"]:
ioc = self.indicators.check_email(result["participant_email"])
if ioc:
result["matched_indicator"] = ioc
self.detected.append(result)
continue
# Custom check for Quadream exploit
if result["summary"] == "Meeting" and result["description"] == "Notes":
self.log.warning("Potential Quadream exploit event identified: %s", result["uuid"])
self.detected.append(result)
def _parse_calendar_db(self):
"""
Parse the calendar database
"""
conn = sqlite3.connect(self.file_path)
cur = conn.cursor()
cur.execute("""
SELECT
CalendarItem.ROWID as "id",
CalendarItem.summary as "summary",
CalendarItem.description as "description",
CalendarItem.start_date as "start_date",
CalendarItem.end_date as "end_date",
CalendarItem.all_day as "all_day",
CalendarItem.calendar_id as "calendar_id",
CalendarItem.organizer_id as "organizer_id",
CalendarItem.url as "url",
CalendarItem.last_modified as "last_modified",
CalendarItem.external_id as "external_id",
CalendarItem.external_mod_tag as "external_mod_tag",
CalendarItem.unique_identifier as "unique_identifier",
CalendarItem.hidden as "hidden",
CalendarItem.UUID as "uuid",
CalendarItem.creation_date as "creation_date",
CalendarItem.action as "action",
CalendarItem.created_by_id as "created_by_id",
Participant.UUID as "participant_uuid",
Participant.email as "participant_email",
Participant.phone_number as "participant_phone",
Participant.comment as "participant_comment",
Participant.last_modified as "participant_last_modified"
FROM CalendarItem
LEFT JOIN Participant ON Participant.ROWID = CalendarItem.organizer_id;
""")
names = [description[0] for description in cur.description]
for item in cur:
entry = {}
for index, value in enumerate(item):
if names[index] in self.timestamps:
if value is None or isinstance(value, str):
entry[names[index]] = value
else:
entry[names[index]] = convert_mactime_to_iso(value)
else:
entry[names[index]] = value
self.results.append(entry)
cur.close()
conn.close()
def run(self) -> None:
self._find_ios_database(backup_ids=CALENDAR_BACKUP_IDS,
root_paths=CALENDAR_ROOT_PATHS)
self.log.info("Found calendar database at path: %s",
self.file_path)
self._parse_calendar_db()
self.log.info("Extracted a total of %d calendar items",
len(self.results))

View File

@ -0,0 +1,34 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2023 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
from mvt.common.indicators import Indicators
from mvt.common.module import run_module
from mvt.ios.modules.mixed.calendar import Calendar
from ..utils import get_ios_backup_folder
class TestCalendarModule:
def test_calendar(self):
m = Calendar(target_path=get_ios_backup_folder())
run_module(m)
assert len(m.results) == 1
assert len(m.timeline) == 4
assert len(m.detected) == 0
assert m.results[0]["summary"] == "Super interesting meeting"
def test_calendar_detection(self, indicator_file):
m = Calendar(target_path=get_ios_backup_folder())
ind = Indicators(log=logging.getLogger())
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["emails"].append("user@example.org")
m.indicators = ind
run_module(m)
assert len(m.results) == 1
assert len(m.timeline) == 4
assert len(m.detected) == 1

View File

@ -17,8 +17,8 @@ class TestFilesystem:
def test_filesystem(self):
m = Filesystem(target_path=get_ios_backup_folder())
run_module(m)
assert len(m.results) == 12
assert len(m.timeline) == 12
assert len(m.results) == 14
assert len(m.timeline) == 14
assert len(m.detected) == 0
def test_detection(self, indicator_file):
@ -29,6 +29,6 @@ class TestFilesystem:
ind.ioc_collections[0]["processes"].append("64d0019cb3d46bfc8cce545a8ba54b93e7ea9347")
m.indicators = ind
run_module(m)
assert len(m.results) == 12
assert len(m.timeline) == 12
assert len(m.results) == 14
assert len(m.timeline) == 14
assert len(m.detected) == 1