Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 87 additions & 40 deletions scripts/artifacts/firefox.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,83 @@
from scripts.artifact_report import ArtifactHtmlReport
from scripts.ilapfuncs import logfunc, tsv, timeline, is_platform_windows, open_sqlite_db_readonly

def table_exists(cursor, table_name):
"""Check if a table exists in the database"""
cursor.execute(
"""
SELECT name FROM sqlite_master
WHERE type='table' AND name=?
""",
(table_name,),
)
return cursor.fetchone() is not None


def get_firefox(files_found, report_folder, seeker, wrap_text):

for file_found in files_found:
file_found = str(file_found)
if not os.path.basename(file_found) == 'places.sqlite': # skip -journal and other files
continue

db = open_sqlite_db_readonly(file_found)
cursor = db.cursor()
cursor.execute('''
SELECT
datetime(moz_places.last_visit_date_local/1000, 'unixepoch') AS LastVisitDate,
moz_places.url AS URL,
moz_places.title AS Title,
moz_places.visit_count_local AS VisitCount,
moz_places.description AS Description,
CASE
WHEN moz_places.hidden = 0 THEN 'No'
WHEN moz_places.hidden = 1 THEN 'Yes'
END AS Hidden,
CASE
WHEN moz_places.typed = 0 THEN 'No'
WHEN moz_places.typed = 1 THEN 'Yes'
END AS Typed,
moz_places.frecency AS Frecency,
moz_places.preview_image_url AS PreviewImageURL
FROM
moz_places
INNER JOIN moz_historyvisits ON moz_places.origin_id = moz_historyvisits.id
INNER JOIN moz_places_metadata ON moz_places.id = moz_places_metadata.id
ORDER BY
moz_places.last_visit_date_local ASC
''')

has_metadata_table = table_exists(cursor, "moz_places_metadata")

if has_metadata_table:
cursor.execute(
"""
SELECT
datetime(moz_places.last_visit_date_local/1000, 'unixepoch') AS LastVisitDate,
moz_places.url AS URL,
moz_places.title AS Title,
moz_places.visit_count_local AS VisitCount,
moz_places.description AS Description,
CASE
WHEN moz_places.hidden = 0 THEN 'No'
WHEN moz_places.hidden = 1 THEN 'Yes'
END AS Hidden,
CASE
WHEN moz_places.typed = 0 THEN 'No'
WHEN moz_places.typed = 1 THEN 'Yes'
END AS Typed,
moz_places.frecency AS Frecency,
moz_places.preview_image_url AS PreviewImageURL
FROM
moz_places
INNER JOIN moz_historyvisits ON moz_places.origin_id = moz_historyvisits.id
INNER JOIN moz_places_metadata ON moz_places.id = moz_places_metadata.id
ORDER BY
moz_places.last_visit_date_local ASC
"""
)
else:
cursor.execute(
"""
SELECT
datetime(moz_places.last_visit_date_local/1000, 'unixepoch') AS LastVisitDate,
moz_places.url AS URL,
moz_places.title AS Title,
moz_places.visit_count_local AS VisitCount,
moz_places.description AS Description,
CASE
WHEN moz_places.hidden = 0 THEN 'No'
WHEN moz_places.hidden = 1 THEN 'Yes'
END AS Hidden,
CASE
WHEN moz_places.typed = 0 THEN 'No'
WHEN moz_places.typed = 1 THEN 'Yes'
END AS Typed,
moz_places.frecency AS Frecency,
moz_places.preview_image_url AS PreviewImageURL
FROM
moz_places
WHERE moz_places.last_visit_date_local IS NOT NULL
ORDER BY
moz_places.last_visit_date_local ASC
"""
)

all_rows = cursor.fetchall()
usageentries = len(all_rows)
Expand All @@ -52,15 +96,15 @@ def get_firefox(files_found, report_folder, seeker, wrap_text):

report.write_artifact_data_table(data_headers, data_list, file_found)
report.end_artifact_report()

tsvname = f'Firefox - Web History'
tsv(report_folder, data_headers, data_list, tsvname)

tlactivity = f'Firefox - Web History'
timeline(report_folder, tlactivity, data_list, data_headers)
else:
logfunc('No Firefox - Web History data available')

cursor = db.cursor()
cursor.execute('''
SELECT
Expand Down Expand Up @@ -104,15 +148,15 @@ def get_firefox(files_found, report_folder, seeker, wrap_text):

report.write_artifact_data_table(data_headers, data_list, file_found)
report.end_artifact_report()

tsvname = f'Firefox - Web Visits'
tsv(report_folder, data_headers, data_list, tsvname)

tlactivity = f'Firefox - Web Visits'
timeline(report_folder, tlactivity, data_list, data_headers)
else:
logfunc('No Firefox - Web Visits data available')

cursor = db.cursor()
cursor.execute('''
SELECT
Expand Down Expand Up @@ -147,15 +191,16 @@ def get_firefox(files_found, report_folder, seeker, wrap_text):

report.write_artifact_data_table(data_headers, data_list, file_found)
report.end_artifact_report()

tsvname = f'Firefox - Bookmarks'
tsv(report_folder, data_headers, data_list, tsvname)

tlactivity = f'Firefox - Bookmarks'
timeline(report_folder, tlactivity, data_list, data_headers)
else:
logfunc('No Firefox - Bookmarks data available')


if table_exists(cursor, "moz_places_metadata_search_queries"):
cursor = db.cursor()
cursor.execute('''
SELECT
Expand All @@ -178,18 +223,20 @@ def get_firefox(files_found, report_folder, seeker, wrap_text):

report.write_artifact_data_table(data_headers, data_list, file_found)
report.end_artifact_report()

tsvname = f'Firefox - Search Terms'
tsv(report_folder, data_headers, data_list, tsvname)

else:
logfunc('No Firefox - Search Terms data available')

db.close()
else:
logfunc('No Firefox - Search Terms data available')

db.close()

__artifacts__ = {
"Firefox": (
"Firefox",
('*/org.mozilla.firefox/files/places.sqlite*'),
get_firefox)
}
}
54 changes: 41 additions & 13 deletions scripts/artifacts/notificationHistory.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,21 @@ def get_notificationHistory(files_found, report_folder, seeker, wrap_text):
multi_root = True
tree = abxread(file_found, multi_root)
else:
tree = ET.parse(file_found)

try:
tree = ET.parse(file_found)
except ET.ParseError:
with open(file_found, "r", encoding="utf-8", errors="ignore") as f:
raw = f.read()
cut_point = raw.rfind("</settings>")
if cut_point != -1:
clean_xml = raw[
: cut_point + 11
]
tree = ET.ElementTree(ET.fromstring(clean_xml))
else:
logfunc(f"Error: Could not recover {file_name}")
continue

root = tree.getroot()
for setting in root.findall(".//setting"):
if setting.attrib.get('name') == 'notification_history_enabled':
Expand All @@ -58,22 +71,37 @@ def get_notificationHistory(files_found, report_folder, seeker, wrap_text):
data_headers = ('Status', 'User')
report.write_artifact_data_table(data_headers, data_list, file_found)
report.end_artifact_report()

tsvname = f'Android Notification History - Status'
tsv(report_folder, data_headers, data_list, tsvname)

else:
logfunc('No Android Notification History - Status data available')

#parsing notification_policy.xml
if file_name.endswith('notification_policy.xml'):
elif file_name.endswith('notification_policy.xml'):
data_list = []
if (checkabx(file_found)):
multi_root = False
tree = abxread(file_found, multi_root)
else:
tree = ET.parse(file_found)

try:
tree = ET.parse(file_found)
except ET.ParseError:
with open(file_found, "r", encoding="utf-8", errors="ignore") as f:
raw = f.read()
if "</notification-policy>" in raw:
clean_xml = raw[: raw.rfind("</notification-policy>") + 22]
tree = ET.ElementTree(ET.fromstring(clean_xml))
elif "</snoozed-notifications>" in raw:
clean_xml = raw[
: raw.rfind("</snoozed-notifications>") + 24
]
tree = ET.ElementTree(ET.fromstring(clean_xml))
else:
logfunc(f"Error: Could not recover {file_name}")
continue

root = tree.getroot()
for elem in root:
if elem.tag == 'snoozed-notifications':
Expand All @@ -94,10 +122,10 @@ def get_notificationHistory(files_found, report_folder, seeker, wrap_text):
data_headers = ('Reminder Time', 'Snoozed Notification')
report.write_artifact_data_table(data_headers, data_list, file_found)
report.end_artifact_report()

tsvname = f'Android Notification History - Snoozed notifications'
tsv(report_folder, data_headers, data_list, tsvname)

else:
logfunc('No Android Notification History - Snoozed notifications data available')

Expand All @@ -116,7 +144,7 @@ def get_notificationHistory(files_found, report_folder, seeker, wrap_text):
major_version = notification_history.major_version if notification_history.HasField('major_version') else None # notification format version should be 1
for notification in notification_history.notification:
package_name = notification.package if notification.package else package_map.get(notification.package_index, "") #retrieves package from the map if not stored locally

#this block tries to fetch the value of each field from within the parsed protobuf file e.g. variable user_id -> recovers the user_id field from the pb
fields = ['uid', 'user_id', 'package_index', 'channel_name', 'channel_id','channel_id_index', 'channel_name_index', 'conversation_id', 'conversation_id_index']
defaults = {field: 'Error' for field in fields}
Expand Down Expand Up @@ -172,10 +200,10 @@ def get_notificationHistory(files_found, report_folder, seeker, wrap_text):
file_directory = os.path.dirname(file_found)
report.write_artifact_data_table(data_headers, data_pb_list, file_directory, html_escape=False)
report.end_artifact_report()

tsvname = f'Android Notification History - Notifications'
tsv(report_folder, data_headers, data_pb_list, tsvname)

tlactivity = f'Android Notification History - Notifications'
timeline(report_folder, tlactivity, data_pb_list, data_headers)
else:
Expand Down
Loading
Loading