예제 #1
0
def parse_sidebarplists(headers, output):
    sidebar_plists = multiglob(
        inputdir, ['Users/*/Library/Preferences/com.apple.sidebarlists.plist'])

    for sblist in sidebar_plists:
        try:
            data = read_bplist(sblist)[0]
        except Exception, e:
            log.debug('Could not parse sidebarplist {0}: {1}'.format(
                sblist, [traceback.format_exc()]))
            data = None

        if data:
            for i in data['systemitems']['VolumesList']:
                record = OrderedDict((h, '') for h in headers)
                record['src_file'] = sblist
                record['src_name'] = "SidebarPlist"
                try:
                    record['name'] = i['Name'].encode('utf-8')
                    if 'Bookmark' in i:
                        record['url'] = 'file:///' + str(i['Bookmark']).split(
                            'file:///')[1].split('\x00')[0]
                    record['source_key'] = 'VolumesList'
                except:
                    log.debug(
                        "Could not parse sidebarplist item: {0}".format(i))
                output.write_entry(record.values())
예제 #2
0
def parse_sandboxed_loginitems(headers, output):
    sandboxed_loginitems = multiglob(
        inputdir, ['var/db/com.apple.xpc.launchd/disabled.*.plist'])

    for i in sandboxed_loginitems:
        record = OrderedDict((h, '') for h in headers)
        metadata = stats2(i, oMACB=True)
        record.update(metadata)
        record['src_file'] = i
        record['src_name'] = "sandboxed_loginitems"

        try:
            p = plistlib.readPlist(i)
        except:
            try:
                p = read_bplist(i)
            except:
                log.debug('Could not read plist {0}: {1}'.format(
                    i, [traceback.format_exc()]))
                p = 'ERROR'

        if p != 'ERROR':
            for k, v in p.items():
                if v is False:
                    record['prog_name'] = k
                    output.write_entry(record.values())
        else:
            errors = {
                k: 'ERROR-CNR-PLIST'
                for k, v in record.items() if v == ''
            }
            record.update(errors)
예제 #3
0
def pull_download_history(downloads_plist, user, downloads_output, downloads_headers):

    log.debug("Trying to access Downloads.plist...")

    if not os.path.exists(downloads_plist):
        log.debug("File not found: {0}".format(downloads_plist))
        return

    try:
        downloads = read_bplist(downloads_plist)[0]['DownloadHistory']
        log.debug("Success. Found {0} lines of data.".format(len(downloads)))
    except IOError:
        log.error("File not found: {0}".format(downloads_plist))
        downloads = []

    log.debug("Parsing and writing downloads data...")
    for i in downloads:
        for k,v in i.items():
            if k not in ['DownloadEntryPostBookmarkBlob','DownloadEntryBookmarkBlob']:
                record = OrderedDict((h, '') for h in downloads_headers)
                record['user'] = user
                record['download_url'] = i['DownloadEntryURL']
                record['download_path'] = i['DownloadEntryPath']
                record['download_started'] = str(i['DownloadEntryDateAddedKey'])
                record['download_finished'] = str(i['DownloadEntryDateFinishedKey'])
                record['download_totalbytes'] = int(i['DownloadEntryProgressTotalToLoad'])
                record['download_bytes_received'] = int(i['DownloadEntryProgressBytesSoFar'])

        downloads_output.write_entry(record.values())

    log.debug("Done.")
예제 #4
0
def parse_finderplists(headers, output):
    finder_plists = multiglob(
        inputdir, ['Users/*/Library/Preferences/com.apple.finder.plist'])

    for fplist in finder_plists:
        try:
            data = read_bplist(fplist)[0]
        except Exception, e:
            log.debug('Could not parse finderplist {0}: {1}'.format(
                fplist, [traceback.format_exc()]))
            data = None

        if data:
            try:
                recentfolders = data['FXRecentFolders']
            except KeyError:
                log.debug("Could not find FXRecentFolders key in plist.")
                recentfolders = []

            try:
                moveandcopy = data['RecentMoveAndCopyDestinations']
            except KeyError:
                log.debug("Could not find FXRecentFolders key in plist.")
                moveandcopy = []

            for i in recentfolders:
                record = OrderedDict((h, '') for h in headers)
                record['src_file'] = fplist
                record['src_name'] = "FinderPlist"
                try:
                    record['source_key'] = 'FXRecentFolders'
                    record['name'] = i['name'].encode('utf-8')
                    bkmk = i['file-bookmark']
                    record['url'] = 'file:///' + str(bkmk).split(
                        ';')[-1].split('\x00')[0]
                except Exception, e:
                    log.debug(
                        "Could not parse finderplist item: {0}".format(i))
                output.write_entry(record.values())

            for i in moveandcopy:
                record = OrderedDict((h, '') for h in headers)
                record['src_file'] = fplist
                record['src_name'] = fplist
                try:
                    record['url'] = i
                    record['name'] = i.split('/')[-2].encode('utf-8')
                    record['source_key'] = 'RecentMoveAndCopyDestinations'
                except Exception, e:
                    log.debug(
                        "Could not parse finderplist item: {0}: {1}".format(
                            i, [traceback.format_exc()]))
                output.write_entry(record.values())
예제 #5
0
def module():
    # KEEP THIS - ENABLES WRITING OUTPUT FILE.
    _output = data_writer(_modName, _headers)

    # -------------BEGIN MODULE-SPECIFIC LOGIC------------- #
    globalpreferences = read_bplist(
        os.path.join(inputdir, 'Library/Preferences/.GlobalPreferences.plist'))
    preferences = plistlib.readPlist(
        os.path.join(
            inputdir,
            'Library/Preferences/SystemConfiguration/preferences.plist'))
    systemversion = plistlib.readPlist(
        os.path.join(inputdir,
                     'System/Library/CoreServices/SystemVersion.plist'))

    # KEEP THE LINE BELOW TO GENERATE AN ORDEREDDICT BASED ON THE HEADERS
    record = OrderedDict((h, '') for h in _headers)

    record['local_hostname'] = finditem(preferences, 'LocalHostName')
    record['ipaddress'] = full_prefix.split(',')[2]

    computer_name = finditem(preferences, 'ComputerName')
    if computer_name is not None:
        record['computer_name'] = computer_name.encode('utf-8')
    record['hostname'] = finditem(preferences, 'HostName')
    record['model'] = finditem(preferences, 'Model')
    record['product_version'] = OSVersion
    record['product_build_version'] = finditem(systemversion,
                                               'ProductBuildVersion')

    g = glob.glob(
        os.path.join(
            inputdir,
            'private/var/folders/zz/zyxvpxvq6csfxvn_n00000sm00006d/C/*'))
    check_dbs = [
        'consolidated.db', 'cache_encryptedA.db', 'lockCache_encryptedA.db'
    ]
    serial_dbs = [
        loc for loc in g if any(loc.endswith(db) for db in check_dbs)
    ]
    serial_query = 'SELECT SerialNumber FROM TableInfo;'

    for db in serial_dbs:
        try:
            cursor = sqlite3.connect(db).cursor()
            record['serial_no'] = cursor.execute(serial_query).fetchone()[0]
            break

        except Exception, e:
            log.debug("Could not get serial number from {0}: {1}".format(
                db, [traceback.format_exc()]))
            record['serial_no'] = 'ERROR'
예제 #6
0
def module():
    headers = ['mtime', 'atime', 'ctime', 'btime', 'date_deleted', 'uniq_id', 'user', 'real_name', 'admin', 'lastloggedin_user']
    output = data_writer(_modName, headers)


    # Parse the com.apple.preferences.accounts.plist to identify deleted accounts.
    _deletedusers_plist = os.path.join(inputdir, 'Library/Preferences/com.apple.preferences.accounts.plist')
    log.debug("Getting deleted users metadata.")
    if not os.path.exists(_deletedusers_plist):
        log.debug("File not found: {0}".format(_deletedusers_plist))
        _deletedusers = []
    else:
        try:
            _deletedusers = read_bplist(_deletedusers_plist)[0]['deletedUsers']
        except Exception, e:
            log.debug("Could not parse: {0}".format(_deletedusers_plist))
            _deletedusers = []
예제 #7
0
def parse_loginitems(headers, output):
    user_loginitems_plist = multiglob(
        inputdir, ['Users/*/Library/Preferences/com.apple.loginitems.plist'])

    for i in user_loginitems_plist:
        record = OrderedDict((h, '') for h in headers)
        metadata = stats2(i, oMACB=True)
        record.update(metadata)
        record['src_file'] = i
        record['src_name'] = "login_items"

        try:
            p = plistlib.readPlist(i)
        except:
            try:
                p = read_bplist(i)
            except:
                log.debug('Could not read plist {0}: {1}'.format(
                    i, [traceback.format_exc()]))
                p = 'ERROR'

        if p != 'ERROR':
            items = p[0]['SessionItems']['CustomListItems']
            for i in items:
                record['prog_name'] = i['Name']
                if 'Alias' in i:
                    try:
                        alias_bin = i['Alias']
                    except:
                        alias_bin = 'ERROR'

                    if alias_bin != 'ERROR':
                        c = [i.encode('hex') for i in alias_bin]
                        for i in range(len(c)):
                            l = int(c[i], 16)
                            if l < len(c) and l > 2:
                                test = os.path.join(inputdir, (''.join(
                                    c[i + 1:i + l + 1])).decode('hex'))
                                try:
                                    if not os.path.exists(test):
                                        continue
                                    else:
                                        record['program'] = test
                                        cs_check_path = os.path.join(
                                            inputdir, test.lstrip('/'))
                                        record['code_signatures'] = str(
                                            get_codesignatures(
                                                cs_check_path, ncs))

                                except:
                                    continue
                                    record['program'] = 'ERROR'
                                    record['code_signatures'] = 'ERROR'

                elif 'Bookmark' in i:
                    try:
                        bookmark_bin = i['Bookmark']
                    except:
                        bookmark_bin = 'ERROR'

                    if bookmark_bin != 'ERROR':
                        program = [i.encode('hex') for i in bookmark_bin]
                        data = Bookmark.from_bytes(
                            ''.join(program).decode('hex'))
                        d = data.get(0xf081, default=None)
                        d = ast.literal_eval(str(d).replace('Data', ''))
                        if d is not None:
                            prog = d.split(';')[-1].replace('\x00', '')
                            record['program'] = prog
                            cs_check_path = os.path.join(
                                inputdir, prog.lstrip('/'))
                            record['code_signatures'] = str(
                                get_codesignatures(cs_check_path, ncs))

                output.write_entry(record.values())
        else:
            errors = {
                k: 'ERROR-CNR-PLIST'
                for k, v in record.items() if v == ''
            }
            record.update(errors)
예제 #8
0
def parse_LaunchAgentsDaemons(headers, output):
    LaunchAgents = multiglob(inputdir, [
        'System/Library/LaunchAgents/*.plist', 'Library/LaunchAgents/*.plist',
        'Users/*/Library/LaunchAgents/*.plist',
        'System/Library/LaunchAgents/.*.plist',
        'Library/LaunchAgents/.*.plist',
        'Users/*/Library/LaunchAgents/.*.plist'
    ])
    LaunchDaemons = multiglob(inputdir, [
        'System/Library/LaunchDaemons/*.plist',
        'Library/LaunchDaemons/*.plist',
        'System/Library/LaunchDaemons/.*.plist',
        'Library/LaunchDaemons/.*.plist'
    ])

    for i in LaunchDaemons + LaunchAgents:

        record = OrderedDict((h, '') for h in headers)
        metadata = stats2(i, oMACB=True)
        record.update(metadata)
        record['src_file'] = i
        record['src_name'] = "launch_items"

        try:
            p = plistlib.readPlist(i)
        except:
            try:
                p = read_bplist(i)
            except:
                log.debug('Could not read plist {0}: {1}'.format(
                    i, [traceback.format_exc()]))
                p = 'ERROR'

        if p != 'ERROR':
            if type(p) is list and len(p) > 0:
                p = p[0]

            # Try to get Label from each plist.
            try:
                record['prog_name'] = p['Label']
            except KeyError:
                log.debug("Cannot extract 'Label' from plist: {0}".format(i))
                record['prog_name'] = 'ERROR'

            # Try to get ProgramArguments if present, or Program, from each plist.
            try:
                prog_args = p['ProgramArguments']
                program = p['ProgramArguments'][0]
                record['program'] = program

                if len(prog_args) > 1:
                    record['args'] = ' '.join(p['ProgramArguments'][1:])
            except (KeyError, IndexError), e:
                try:
                    program = p['Program']
                    record['program'] = program
                except:
                    log.debug(
                        "Cannot extract 'Program' or 'ProgramArguments' from plist: {0}"
                        .format(i))
                    program = None
                    record['program'] = 'ERROR'
                    record['args'] = 'ERROR'
            except Exception, e:
                log.debug('Could not parse plist {0}: {1}'.format(
                    i, [traceback.format_exc()]))
                program = None

            # If program is ID'd, run additional checks.
            if program:
                cs_check_path = os.path.join(inputdir, program.lstrip('/'))
                record['code_signatures'] = str(
                    get_codesignatures(cs_check_path, ncs))

                hashset = get_hashes(program)
                record['sha256'] = hashset['sha256']
                record['md5'] = hashset['md5']
예제 #9
0
def pull_visit_history(recently_closed_plist, history_db, user, history_output,
                       history_headers):

    try:
        log.debug("Trying to access RecentlyClosedTabs.plist...")
        recently_closed = read_bplist(
            recently_closed_plist)[0]['ClosedTabOrWindowPersistentStates']
        d = {}
        log.debug("Success. Found {0} lines of data.".format(
            len(recently_closed)))
        for i in recently_closed:
            for k, v in i['PersistentState'].items():
                if k == 'TabURL':
                    tab_title = i['PersistentState']['TabTitle'].encode(
                        'utf-8')
                    date_closed = parser.parse(
                        str(i['PersistentState']['DateClosed'])).replace(
                            tzinfo=None).isoformat() + 'Z'
                    try:
                        last_visit_time = i['PersistentState']['LastVisitTime']
                        last_visit_time = cocoa_time(last_visit_time)
                    except KeyError:
                        last_visit_time = ''
                    d[i['PersistentState']['TabURL']] = [
                        tab_title, date_closed, last_visit_time
                    ]
    except IOError:
        log.debug("File not found: {0}".format(recently_closed_plist))
        d = {}
        pass

    desired_columns = ['visit_time', 'title', 'url', 'visit_count']
    available_columns = get_column_headers(
        history_db, 'history_visits') + get_column_headers(
            history_db, 'history_items')
    query_columns_list = [i for i in desired_columns if i in available_columns]
    query_columns = ', '.join(
        [i for i in desired_columns if i in available_columns])

    unavailable = ','.join(
        list(set(desired_columns) - set(query_columns_list)))
    if len(unavailable) > 0:
        log.debug(
            'The following desired columns are not available in the database: {0}'
            .format(unavailable))

    log.debug("Executing sqlite query for visit history...")

    try:
        history_data = sqlite3.connect(history_db).cursor().execute(
            'SELECT {0} from history_visits \
            left join history_items on history_items.id = history_visits.history_item'
            .format(query_columns)).fetchall()

        log.debug("Success. Found {0} lines of data.".format(
            len(history_data)))

    except sqlite3.OperationalError:
        error = [
            x for x in traceback.format_exc().split('\n')
            if x.startswith("OperationalError")
        ]
        log.error('Failed to run query. [{0}]'.format(error[0]))

        return

    log.debug("Parsing and writing visits data...")

    nondict = dict.fromkeys(desired_columns)
    for item in history_data:
        record = OrderedDict((h, '') for h in history_headers)
        item_dict = dict(zip(query_columns_list, item))
        nondict.update(item_dict)

        record['user'] = user
        record['visit_time'] = cocoa_time(nondict['visit_time'])
        if nondict['title'] is not None:
            record['title'] = nondict['title'].encode('utf-8')
        record['url'] = nondict['url']
        record['visit_count'] = nondict['visit_count']

        if nondict['url'] in d.keys():

            record['recently_closed'] = 'Yes'
            record['tab_title'] = d[nondict['url']][0]
            record['date_closed'] = d[nondict['url']][1]
            record['last_visit_time'] = d[nondict['url']][2]

        history_output.write_entry(record.values())
예제 #10
0
            and i not in ['daemon.plist', 'nobody.plist']
        ]
    except OSError:
        log.debug("Could not connect [{0}].".format([traceback.format_exc()]))
        _liveplists = []

    _liveusers = glob.glob((os.path.join(inputdir, 'Users/*')))
    _liveusers.append(os.path.join(inputdir, "var/root"))

    _admins = os.path.join(
        inputdir, 'private/var/db/dslocal/nodes/Default/groups/admin.plist')
    if not os.path.exists(_admins):
        log.debug("File not found: {0}".format(_admins))
    else:
        try:
            admins = list(read_bplist(_admins)[0]['users'])
            foo
        except Exception, e:
            try:
                # dscl . -read /Groups/admin GroupMembership
                admin_users, e = subprocess.Popen(
                    ["dscl", ".", "-read", "/Groups/admin", "GroupMembership"],
                    stdout=subprocess.PIPE).communicate()
                admins = admin_users.split()[1:]
            except:
                admins = []
                log.debug("Could not parse: {0}".format(_admins))

    _loginwindow = os.path.join(
        inputdir, 'Library/Preferences/com.apple.loginwindow.plist')
    if not os.path.exists(_loginwindow):
예제 #11
0
    for i in range(len(_deletedusers)):
        record = OrderedDict((h, '') for h in headers)
        record['date_deleted'] = parser.parse(str(_deletedusers[i]['date'])).strftime('%Y-%m-%dT%H:%M:%SZ')
        record['uniq_id'] = _deletedusers[i]['dsAttrTypeStandard:UniqueID']
        record['user'] = _deletedusers[i]['name']
        record['real_name'] = _deletedusers[i]['dsAttrTypeStandard:RealName']

        output.write_entry(record.values())

    # Try to determine admin users on the system.
    _admins = os.path.join(inputdir, 'private/var/db/dslocal/nodes/Default/groups/admin.plist')
    log.debug("Getting admin users metadata.")
    try:
        # Should work on forensic images and live systems under Mojave.
        admins = list(read_bplist(_admins)[0]['users'])
    except:
        log.debug("Could not access dslocal: [{0}].".format([traceback.format_exc()]))
        if not forensic_mode:
            try:
                log.debug("Trying DSCL to obtain admin users as input is live system.")
                admin_users, e = subprocess.Popen(["dscl", ".", "-read", "/Groups/admin", "GroupMembership"], stdout=subprocess.PIPE).communicate()
                admins = admin_users.split()[1:]
            except:
                admins = []
                log.debug("Could not parse: {0}".format(_admins))
                log.error("Could not determine admin users.")
        else:
            log.error('Could not determine admin users from forensic image.')
            admins = []