Open all iOS sqlite3 databases with immutable=1

This commit is contained in:
Rory Flynn 2023-11-27 17:55:44 +01:00
parent fb52f73556
commit 571117ea53
22 changed files with 29 additions and 45 deletions

View File

@ -8,7 +8,6 @@ import io
import logging import logging
import os import os
import plistlib import plistlib
import sqlite3
from typing import Optional from typing import Optional
from mvt.common.module import DatabaseNotFoundError from mvt.common.module import DatabaseNotFoundError
@ -124,7 +123,7 @@ class Manifest(IOSExtraction):
self.log.info("Found Manifest.db database at path: %s", manifest_db_path) 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 = conn.cursor()
cur.execute("SELECT * FROM Files;") cur.execute("SELECT * FROM Files;")

View File

@ -49,7 +49,7 @@ class IOSExtraction(MVTModule):
""" """
# TODO: Find a better solution. # TODO: Find a better solution.
if not forced: if not forced:
conn = sqlite3.connect(file_path) conn = self._open_sqlite_db(file_path)
cur = conn.cursor() cur = conn.cursor()
try: try:
@ -91,6 +91,9 @@ class IOSExtraction(MVTModule):
self.log.info("Database at path %s recovered successfully!", file_path) 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( def _get_backup_files_from_manifest(
self, relative_path: Optional[str] = None, domain: Optional[str] = None self, relative_path: Optional[str] = None, domain: Optional[str] = None
) -> Iterator[dict]: ) -> Iterator[dict]:
@ -109,7 +112,7 @@ class IOSExtraction(MVTModule):
base_sql = "SELECT fileID, domain, relativePath FROM Files WHERE " base_sql = "SELECT fileID, domain, relativePath FROM Files WHERE "
try: try:
conn = sqlite3.connect(manifest_db_path) conn = self._open_sqlite_db(manifest_db_path)
cur = conn.cursor() cur = conn.cursor()
if relative_path and domain: if relative_path and domain:
cur.execute( cur.execute(

View File

@ -85,7 +85,7 @@ class Analytics(IOSExtraction):
def _extract_analytics_data(self): def _extract_analytics_data(self):
artifact = self.file_path.split("/")[-1] artifact = self.file_path.split("/")[-1]
conn = sqlite3.connect(self.file_path) conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor() cur = conn.cursor()
try: try:

View File

@ -64,7 +64,7 @@ class CacheFiles(IOSExtraction):
def _process_cache_file(self, file_path): def _process_cache_file(self, file_path):
self.log.info("Processing cache file at path: %s", 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() cur = conn.cursor()
try: try:

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import sqlite3
from typing import Optional, Union from typing import Optional, Union
from mvt.common.utils import convert_mactime_to_iso from mvt.common.utils import convert_mactime_to_iso
@ -61,7 +60,7 @@ class SafariFavicon(IOSExtraction):
self.detected.append(result) self.detected.append(result)
def _process_favicon_db(self, file_path): def _process_favicon_db(self, file_path):
conn = sqlite3.connect(file_path) conn = self._open_sqlite_db(file_path)
# Fetch valid icon cache. # Fetch valid icon cache.
cur = conn.cursor() cur = conn.cursor()

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import sqlite3
from typing import Optional, Union from typing import Optional, Union
from mvt.common.utils import convert_mactime_to_iso from mvt.common.utils import convert_mactime_to_iso
@ -82,7 +81,7 @@ class Calendar(IOSExtraction):
""" """
Parse the calendar database Parse the calendar database
""" """
conn = sqlite3.connect(self.file_path) conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor() cur = conn.cursor()
cur.execute( cur.execute(

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import sqlite3
from typing import Optional, Union from typing import Optional, Union
from mvt.common.utils import convert_mactime_to_iso from mvt.common.utils import convert_mactime_to_iso
@ -53,7 +52,7 @@ class Calls(IOSExtraction):
) )
self.log.info("Found Calls database at path: %s", self.file_path) 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 = conn.cursor()
cur.execute( cur.execute(
""" """

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import sqlite3
from typing import Optional, Union from typing import Optional, Union
from mvt.common.utils import convert_chrometime_to_datetime, convert_datetime_to_iso from mvt.common.utils import convert_chrometime_to_datetime, convert_datetime_to_iso
@ -66,7 +65,7 @@ class ChromeFavicon(IOSExtraction):
) )
self.log.info("Found Chrome favicon cache database at path: %s", self.file_path) 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 # Fetch icon cache
cur = conn.cursor() cur = conn.cursor()

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import sqlite3
from typing import Optional, Union from typing import Optional, Union
from mvt.common.utils import convert_chrometime_to_datetime, convert_datetime_to_iso from mvt.common.utils import convert_chrometime_to_datetime, convert_datetime_to_iso
@ -67,7 +66,7 @@ class ChromeHistory(IOSExtraction):
) )
self.log.info("Found Chrome history database at path: %s", self.file_path) 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 = conn.cursor()
cur.execute( cur.execute(
""" """

View File

@ -44,7 +44,7 @@ class Contacts(IOSExtraction):
) )
self.log.info("Found Contacts database at path: %s", self.file_path) 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() cur = conn.cursor()
try: try:
cur.execute( cur.execute(

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import sqlite3
from typing import Optional, Union from typing import Optional, Union
from mvt.common.utils import convert_unix_to_iso from mvt.common.utils import convert_unix_to_iso
@ -68,7 +67,7 @@ class FirefoxFavicon(IOSExtraction):
) )
self.log.info("Found Firefox favicon database at path: %s", self.file_path) 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 = conn.cursor()
cur.execute( cur.execute(
""" """

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import sqlite3
from typing import Optional, Union from typing import Optional, Union
from mvt.common.utils import convert_unix_to_iso from mvt.common.utils import convert_unix_to_iso
@ -68,7 +67,7 @@ class FirefoxHistory(IOSExtraction):
) )
self.log.info("Found Firefox history database at path: %s", self.file_path) 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 = conn.cursor()
cur.execute( cur.execute(
""" """

View File

@ -280,7 +280,7 @@ class InteractionC(IOSExtraction):
) )
self.log.info("Found InteractionC database at path: %s", self.file_path) 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() cur = conn.cursor()
try: try:

View File

@ -76,7 +76,7 @@ class SafariBrowserState(IOSExtraction):
def _process_browser_state_db(self, db_path): def _process_browser_state_db(self, db_path):
self._recover_sqlite_db_if_needed(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() cur = conn.cursor()
try: try:

View File

@ -5,7 +5,6 @@
import logging import logging
import os import os
import sqlite3
from typing import Optional, Union from typing import Optional, Union
from mvt.common.url import URL from mvt.common.url import URL
@ -115,7 +114,7 @@ class SafariHistory(IOSExtraction):
def _process_history_db(self, history_path): def _process_history_db(self, history_path):
self._recover_sqlite_db_if_needed(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 = conn.cursor()
cur.execute( cur.execute(
""" """

View File

@ -83,7 +83,7 @@ class Shortcuts(IOSExtraction):
) )
self.log.info("Found Shortcuts database at path: %s", self.file_path) 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 conn.text_factory = bytes
cur = conn.cursor() cur = conn.cursor()
try: try:

View File

@ -86,7 +86,7 @@ class SMS(IOSExtraction):
self.log.info("Found SMS database at path: %s", self.file_path) self.log.info("Found SMS database at path: %s", self.file_path)
try: try:
conn = sqlite3.connect(self.file_path) conn = self._open_sqlite_db(self.file_path)
cur = conn.cursor() cur = conn.cursor()
cur.execute( cur.execute(
""" """
@ -103,7 +103,7 @@ class SMS(IOSExtraction):
conn.close() conn.close()
if "database disk image is malformed" in str(exc): if "database disk image is malformed" in str(exc):
self._recover_sqlite_db_if_needed(self.file_path, forced=True) 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 = conn.cursor()
cur.execute( cur.execute(
""" """

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import sqlite3
from base64 import b64encode from base64 import b64encode
from typing import Optional, Union from typing import Optional, Union
@ -72,7 +71,7 @@ class SMSAttachments(IOSExtraction):
self._find_ios_database(backup_ids=SMS_BACKUP_IDS, root_paths=SMS_ROOT_PATHS) 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) 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 = conn.cursor()
cur.execute( cur.execute(
""" """

View File

@ -95,7 +95,7 @@ class TCC(IOSExtraction):
self.detected.append(result) self.detected.append(result)
def process_db(self, file_path): def process_db(self, file_path):
conn = sqlite3.connect(file_path) conn = self._open_sqlite_db(file_path)
cur = conn.cursor() cur = conn.cursor()
db_version = "v3" db_version = "v3"
try: try:

View File

@ -73,7 +73,7 @@ class WebkitResourceLoadStatistics(IOSExtraction):
self._recover_sqlite_db_if_needed(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() cur = conn.cursor()
try: try:

View File

@ -4,7 +4,6 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import sqlite3
from typing import Optional, Union from typing import Optional, Union
from mvt.common.utils import check_for_links, convert_mactime_to_iso from mvt.common.utils import check_for_links, convert_mactime_to_iso
@ -69,7 +68,7 @@ class Whatsapp(IOSExtraction):
) )
self.log.info("Found WhatsApp database at path: %s", self.file_path) 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() cur = conn.cursor()
# Query all messages and join tables which can contain media attachments # Query all messages and join tables which can contain media attachments

View File

@ -4,31 +4,23 @@
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
import logging import logging
import pytest
from mvt.common.indicators import Indicators from mvt.common.indicators import Indicators
from mvt.common.module import run_module from mvt.common.module import run_module
from mvt.ios.modules.fs.filesystem import Filesystem from mvt.ios.modules.fs.filesystem import Filesystem
from ..utils import delete_tmp_db_files, get_ios_backup_folder from ..utils import 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
class TestFilesystem: class TestFilesystem:
def test_filesystem(self, cleanup_tmp_artifacts): def test_filesystem(self):
m = Filesystem(target_path=get_ios_backup_folder()) m = Filesystem(target_path=get_ios_backup_folder())
run_module(m) run_module(m)
assert len(m.results) == 15 assert len(m.results) == 15
assert len(m.timeline) == 15 assert len(m.timeline) == 15
assert len(m.detected) == 0 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()) m = Filesystem(target_path=get_ios_backup_folder())
ind = Indicators(log=logging.getLogger()) ind = Indicators(log=logging.getLogger())
ind.parse_stix2(indicator_file) ind.parse_stix2(indicator_file)