-
Notifications
You must be signed in to change notification settings - Fork 367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add SQLite parser for iOS Accounts (Accounts3.sqlite) file #4926
base: main
Are you sure you want to change the base?
Changes from all commits
49888c4
0cf651e
73348df
4de4a11
9f88ed8
4110ff3
3617f66
e2055b5
a73674d
b73ddf7
167f68d
5b6106f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# -*- coding: utf-8 -*- | ||
"""SQLite parser plugin for iOS accounts (Accounts3.db) database files.""" | ||
|
||
from dfdatetime import cocoa_time as dfdatetime_cocoa_time | ||
|
||
from plaso.containers import events | ||
from plaso.parsers import sqlite | ||
from plaso.parsers.sqlite_plugins import interface | ||
|
||
|
||
class IOSAccounts(events.EventData): | ||
"""iOS accounts event data. | ||
|
||
Attributes: | ||
date (dfdatetime.DateTimeValues): date and time the account | ||
was created. | ||
account_type (str): account type. | ||
username (str): user name. | ||
identifier (str): identifier. | ||
owning_bundle_id (str): owning bundle identifier of the app | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style guide nit: id => identifier |
||
managing the account. | ||
""" | ||
|
||
DATA_TYPE = 'ios:accounts:account' | ||
|
||
def __init__(self): | ||
"""Initializes event data.""" | ||
super(IOSAccounts, self).__init__(data_type=self.DATA_TYPE) | ||
self.date = None | ||
self.account_type = None | ||
self.username = None | ||
self.identifier = None | ||
self.owning_bundle_id = None | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style guide nit: have 2 empty lines |
||
class IOSAccountsPlugin(interface.SQLitePlugin): | ||
"""SQLite parser plugin for iOS accounts (Accounts3.db) database files.""" | ||
|
||
NAME = 'ios_accounts' | ||
DATA_FORMAT = 'iOS accounts SQLite database (Accounts3.db) file' | ||
|
||
REQUIRED_STRUCTURE = { | ||
'ZACCOUNT': frozenset([ | ||
'ZACCOUNTTYPE', 'ZDATE', 'ZUSERNAME', 'ZIDENTIFIER', | ||
'ZOWNINGBUNDLEID']), | ||
'ZACCOUNTTYPE': frozenset([ | ||
'Z_PK', 'ZACCOUNTTYPEDESCRIPTION']) | ||
} | ||
|
||
QUERIES = [(( | ||
'SELECT ZACCOUNT.ZDATE, ZACCOUNTTYPE.ZACCOUNTTYPEDESCRIPTION, ' | ||
'ZACCOUNT.ZUSERNAME, ZACCOUNT.ZIDENTIFIER, ZACCOUNT.ZOWNINGBUNDLEID ' | ||
'FROM ZACCOUNT LEFT JOIN ZACCOUNTTYPE ' | ||
'ON ZACCOUNT.ZACCOUNTTYPE = ZACCOUNTTYPE.Z_PK'), | ||
'ParseAccountRow')] | ||
|
||
SCHEMAS = { | ||
'ZACCOUNT': ( | ||
'CREATE TABLE ZACCOUNT (Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, ' | ||
'Z_OPT INTEGER, ZACTIVE INTEGER, ZAUTHENTICATED INTEGER, ' | ||
'ZSUPPORTSAUTHENTICATION INTEGER, ZVISIBLE INTEGER, ' | ||
'ZACCOUNTTYPE INTEGER, ZPARENTACCOUNT INTEGER, ' | ||
'ZDATE TIMESTAMP, ZLASTCREDENTIALRENEWALREJECTIONDATE TIMESTAMP, ' | ||
'ZACCOUNTDESCRIPTION TEXT, ZAUTHENTICATIONTYPE TEXT, ' | ||
'ZCREDENTIALTYPE TEXT, ZIDENTIFIER TEXT, ZOWNINGBUNDLEID TEXT, ' | ||
'ZUSERNAME TEXT, ZDATACLASSPROPERTIES BLOB)'), | ||
'ZACCOUNTTYPE': ( | ||
'CREATE TABLE ZACCOUNTTYPE (Z_PK INTEGER PRIMARY KEY, ' | ||
'Z_ENT INTEGER, Z_OPT INTEGER, ZENCRYPTACCOUNTPROPERTIES INTEGER, ' | ||
'ZOBSOLETE INTEGER, ZSUPPORTSAUTHENTICATION INTEGER, ' | ||
'ZSUPPORTSMULTIPLEACCOUNTS INTEGER, ZVISIBILITY INTEGER, ' | ||
'ZACCOUNTTYPEDESCRIPTION TEXT, ZCREDENTIALPROTECTIONPOLICY TEXT, ' | ||
'ZCREDENTIALTYPE TEXT, ZIDENTIFIER TEXT, ZOWNINGBUNDLEID TEXT)')} | ||
|
||
REQUIRES_SCHEMA_MATCH = False | ||
|
||
def _GetTimeRowValue(self, query_hash, row, value_name): | ||
"""Retrieves a date and time value from the row. | ||
|
||
Args: | ||
query_hash (int): hash of the query, that uniquely | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style guide nit: have 2 indentation spaces |
||
identifies the query that produced the row. | ||
row (sqlite3.Row): row. | ||
value_name (str): name of the value. | ||
|
||
Returns: | ||
dfdatetime.CocoaTime: date and time value or None if not available. | ||
""" | ||
timestamp = self._GetRowValue(query_hash, row, value_name) | ||
if timestamp is None: | ||
return None | ||
|
||
return dfdatetime_cocoa_time.CocoaTime(timestamp=timestamp) | ||
|
||
# pylint: disable=unused-argument | ||
def ParseAccountRow( | ||
self, parser_mediator, query, row, **unused_kwargs): | ||
"""Parses an account row. | ||
|
||
Args: | ||
parser_mediator (ParserMediator): mediates interactions between | ||
parsers and other components, such as storage and dfVFS. | ||
query (str): query that created the row. | ||
row (sqlite3.Row): row. | ||
""" | ||
query_hash = hash(query) | ||
|
||
event_data = IOSAccounts() | ||
event_data.date = self._GetTimeRowValue(query_hash, row, 'ZDATE') | ||
event_data.account_type = self._GetRowValue(query_hash, | ||
row, 'ZACCOUNTTYPEDESCRIPTION') | ||
event_data.username = self._GetRowValue(query_hash, row, 'ZUSERNAME') | ||
event_data.identifier = self._GetRowValue(query_hash, row, | ||
'ZIDENTIFIER') | ||
event_data.owning_bundle_id = self._GetRowValue(query_hash, row, | ||
'ZOWNINGBUNDLEID') | ||
|
||
parser_mediator.ProduceEventData(event_data) | ||
|
||
|
||
sqlite.SQLiteParser.RegisterPlugin(IOSAccountsPlugin) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Tests for the SQLite parser plugin for iOS accounts database files.""" | ||
|
||
import unittest | ||
|
||
from plaso.parsers.sqlite_plugins import ios_accounts | ||
|
||
from tests.parsers.sqlite_plugins import test_lib | ||
|
||
|
||
class IOSAccountsPluginTest(test_lib.SQLitePluginTestCase): | ||
"""Tests for the SQLite parser plugin for iOS accounts database files.""" | ||
|
||
def testParse(self): | ||
"""Tests the ParseAccountRow method.""" | ||
plugin = ios_accounts.IOSAccountsPlugin() | ||
storage_writer = self._ParseDatabaseFileWithPlugin( | ||
['Accounts3.sqlite'], plugin) | ||
|
||
number_of_event_data = storage_writer.GetNumberOfAttributeContainers( | ||
'event_data') | ||
self.assertEqual(number_of_event_data, 18) | ||
|
||
number_of_warnings = storage_writer.GetNumberOfAttributeContainers( | ||
'extraction_warning') | ||
self.assertEqual(number_of_warnings, 0) | ||
|
||
expected_event_values = { | ||
'date': '2020-03-21T21:47:57.068197+00:00', | ||
'account_type': 'iCloud', | ||
'identifier': '1589F4EC-8F6C-4F37-929F-C6F121B36A59', | ||
'owning_bundle_id': 'com.apple.purplebuddy', | ||
'username': '[email protected]' | ||
} | ||
|
||
event_data = storage_writer.GetAttributeContainerByIndex( | ||
'event_data', 3) | ||
self.CheckEventData(event_data, expected_event_values) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for consistency across the codebase changing this to creation_time