-
Notifications
You must be signed in to change notification settings - Fork 918
/
calendar.py
144 lines (125 loc) · 5.04 KB
/
calendar.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2023 The MVT 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 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,
module_options: Optional[dict] = None,
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,
module_options=module_options,
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"] and self.indicators:
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))