Skip to content

Commit

Permalink
Open all iOS sqlite3 databases with immutable=1 (#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
roaree authored Nov 28, 2023
1 parent fb52f73 commit fd3ef76
Show file tree
Hide file tree
Showing 22 changed files with 29 additions and 45 deletions.
3 changes: 1 addition & 2 deletions mvt/ios/modules/backup/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import logging
import os
import plistlib
import sqlite3
from typing import Optional

from mvt.common.module import DatabaseNotFoundError
Expand Down Expand Up @@ -124,7 +123,7 @@ def run(self) -> None:

self.log.info("Found Manifest.db database at path: %s", manifest_db_path)

conn = sqlite3.connect(manifest_db_path)
conn = self._open_sqlite_db(manifest_db_path)
cur = conn.cursor()

cur.execute("SELECT * FROM Files;")
Expand Down
7 changes: 5 additions & 2 deletions mvt/ios/modules/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def _recover_sqlite_db_if_needed(
"""
# TODO: Find a better solution.
if not forced:
conn = sqlite3.connect(file_path)
conn = self._open_sqlite_db(file_path)
cur = conn.cursor()

try:
Expand Down Expand Up @@ -91,6 +91,9 @@ def _recover_sqlite_db_if_needed(

self.log.info("Database at path %s recovered successfully!", file_path)

def _open_sqlite_db(self, file_path: str) -> sqlite3.Connection:
return sqlite3.connect(f"file:{file_path}?immutable=1")

def _get_backup_files_from_manifest(
self, relative_path: Optional[str] = None, domain: Optional[str] = None
) -> Iterator[dict]:
Expand All @@ -109,7 +112,7 @@ def _get_backup_files_from_manifest(
base_sql = "SELECT fileID, domain, relativePath FROM Files WHERE "

try:
conn = sqlite3.connect(manifest_db_path)
conn = self._open_sqlite_db(manifest_db_path)
cur = conn.cursor()
if relative_path and domain:
cur.execute(
Expand Down
2 changes: 1 addition & 1 deletion mvt/ios/modules/fs/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def check_indicators(self) -> None:
def _extract_analytics_data(self):
artifact = self.file_path.split("/")[-1]

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()

try:
Expand Down
2 changes: 1 addition & 1 deletion mvt/ios/modules/fs/cache_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def check_indicators(self) -> None:
def _process_cache_file(self, file_path):
self.log.info("Processing cache file at path: %s", file_path)

conn = sqlite3.connect(file_path)
conn = self._open_sqlite_db(file_path)
cur = conn.cursor()

try:
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/fs/safari_favicon.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/

import logging
import sqlite3
from typing import Optional, Union

from mvt.common.utils import convert_mactime_to_iso
Expand Down Expand Up @@ -61,7 +60,7 @@ def check_indicators(self) -> None:
self.detected.append(result)

def _process_favicon_db(self, file_path):
conn = sqlite3.connect(file_path)
conn = self._open_sqlite_db(file_path)

# Fetch valid icon cache.
cur = conn.cursor()
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/mixed/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/

import logging
import sqlite3
from typing import Optional, Union

from mvt.common.utils import convert_mactime_to_iso
Expand Down Expand Up @@ -82,7 +81,7 @@ def _parse_calendar_db(self):
"""
Parse the calendar database
"""
conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()

cur.execute(
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/mixed/calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/

import logging
import sqlite3
from typing import Optional, Union

from mvt.common.utils import convert_mactime_to_iso
Expand Down Expand Up @@ -53,7 +52,7 @@ def run(self) -> None:
)
self.log.info("Found Calls database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()
cur.execute(
"""
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/mixed/chrome_favicon.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/

import logging
import sqlite3
from typing import Optional, Union

from mvt.common.utils import convert_chrometime_to_datetime, convert_datetime_to_iso
Expand Down Expand Up @@ -66,7 +65,7 @@ def run(self) -> None:
)
self.log.info("Found Chrome favicon cache database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)

# Fetch icon cache
cur = conn.cursor()
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/mixed/chrome_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/

import logging
import sqlite3
from typing import Optional, Union

from mvt.common.utils import convert_chrometime_to_datetime, convert_datetime_to_iso
Expand Down Expand Up @@ -67,7 +66,7 @@ def run(self) -> None:
)
self.log.info("Found Chrome history database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()
cur.execute(
"""
Expand Down
2 changes: 1 addition & 1 deletion mvt/ios/modules/mixed/contacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def run(self) -> None:
)
self.log.info("Found Contacts database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()
try:
cur.execute(
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/mixed/firefox_favicon.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/

import logging
import sqlite3
from typing import Optional, Union

from mvt.common.utils import convert_unix_to_iso
Expand Down Expand Up @@ -68,7 +67,7 @@ def run(self) -> None:
)
self.log.info("Found Firefox favicon database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()
cur.execute(
"""
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/mixed/firefox_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/

import logging
import sqlite3
from typing import Optional, Union

from mvt.common.utils import convert_unix_to_iso
Expand Down Expand Up @@ -68,7 +67,7 @@ def run(self) -> None:
)
self.log.info("Found Firefox history database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()
cur.execute(
"""
Expand Down
2 changes: 1 addition & 1 deletion mvt/ios/modules/mixed/interactionc.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def run(self) -> None:
)
self.log.info("Found InteractionC database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()

try:
Expand Down
2 changes: 1 addition & 1 deletion mvt/ios/modules/mixed/safari_browserstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def check_indicators(self) -> None:

def _process_browser_state_db(self, db_path):
self._recover_sqlite_db_if_needed(db_path)
conn = sqlite3.connect(db_path)
conn = self._open_sqlite_db(db_path)

cur = conn.cursor()
try:
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/mixed/safari_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import logging
import os
import sqlite3
from typing import Optional, Union

from mvt.common.url import URL
Expand Down Expand Up @@ -115,7 +114,7 @@ def check_indicators(self) -> None:

def _process_history_db(self, history_path):
self._recover_sqlite_db_if_needed(history_path)
conn = sqlite3.connect(history_path)
conn = self._open_sqlite_db(history_path)
cur = conn.cursor()
cur.execute(
"""
Expand Down
2 changes: 1 addition & 1 deletion mvt/ios/modules/mixed/shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def run(self) -> None:
)
self.log.info("Found Shortcuts database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
conn.text_factory = bytes
cur = conn.cursor()
try:
Expand Down
4 changes: 2 additions & 2 deletions mvt/ios/modules/mixed/sms.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def run(self) -> None:
self.log.info("Found SMS database at path: %s", self.file_path)

try:
conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()
cur.execute(
"""
Expand All @@ -103,7 +103,7 @@ def run(self) -> None:
conn.close()
if "database disk image is malformed" in str(exc):
self._recover_sqlite_db_if_needed(self.file_path, forced=True)
conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()
cur.execute(
"""
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/mixed/sms_attachments.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/

import logging
import sqlite3
from base64 import b64encode
from typing import Optional, Union

Expand Down Expand Up @@ -72,7 +71,7 @@ def run(self) -> None:
self._find_ios_database(backup_ids=SMS_BACKUP_IDS, root_paths=SMS_ROOT_PATHS)
self.log.info("Found SMS database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()
cur.execute(
"""
Expand Down
2 changes: 1 addition & 1 deletion mvt/ios/modules/mixed/tcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def check_indicators(self) -> None:
self.detected.append(result)

def process_db(self, file_path):
conn = sqlite3.connect(file_path)
conn = self._open_sqlite_db(file_path)
cur = conn.cursor()
db_version = "v3"
try:
Expand Down
2 changes: 1 addition & 1 deletion mvt/ios/modules/mixed/webkit_resource_load_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def _process_observations_db(self, db_path: str, domain: str, path: str) -> None

self._recover_sqlite_db_if_needed(db_path)

conn = sqlite3.connect(db_path)
conn = self._open_sqlite_db(db_path)
cur = conn.cursor()

try:
Expand Down
3 changes: 1 addition & 2 deletions mvt/ios/modules/mixed/whatsapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/

import logging
import sqlite3
from typing import Optional, Union

from mvt.common.utils import check_for_links, convert_mactime_to_iso
Expand Down Expand Up @@ -69,7 +68,7 @@ def run(self) -> None:
)
self.log.info("Found WhatsApp database at path: %s", self.file_path)

conn = sqlite3.connect(self.file_path)
conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor()

# Query all messages and join tables which can contain media attachments
Expand Down
14 changes: 3 additions & 11 deletions tests/ios_fs/test_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,23 @@
# https://license.mvt.re/1.1/
import logging

import pytest

from mvt.common.indicators import Indicators
from mvt.common.module import run_module
from mvt.ios.modules.fs.filesystem import Filesystem

from ..utils import delete_tmp_db_files, get_ios_backup_folder


@pytest.fixture()
def cleanup_tmp_artifacts():
ios_backup_folder = get_ios_backup_folder()
delete_tmp_db_files(ios_backup_folder)
return
from ..utils import get_ios_backup_folder


class TestFilesystem:
def test_filesystem(self, cleanup_tmp_artifacts):
def test_filesystem(self):
m = Filesystem(target_path=get_ios_backup_folder())
run_module(m)
assert len(m.results) == 15
assert len(m.timeline) == 15
assert len(m.detected) == 0

def test_detection(self, indicator_file, cleanup_tmp_artifacts):
def test_detection(self, indicator_file):
m = Filesystem(target_path=get_ios_backup_folder())
ind = Indicators(log=logging.getLogger())
ind.parse_stix2(indicator_file)
Expand Down

0 comments on commit fd3ef76

Please sign in to comment.