def testReadTrailer(self): blank_trailer = StringIO.StringIO() plist = binplist.BinaryPlist(blank_trailer) self.assertRaises(IOError, plist._ReadTrailer) plist = binplist.BinaryPlist(self.minimal) # We allow parsing the minimal plist, even though it's not valid plist._ReadTrailer()
def testReadHeader(self): blank_header = StringIO.StringIO() plist = binplist.BinaryPlist(blank_header) self.assertRaises(binplist.FormatError, plist._ReadHeader) wrong_header = StringIO.StringIO("bla") plist = binplist.BinaryPlist(wrong_header) self.assertRaises(binplist.FormatError, plist._ReadHeader) unknown_version = StringIO.StringIO("bplist99") plist = binplist.BinaryPlist(unknown_version) plist._ReadHeader() self.assertEqual(plist.version, "99") usual_header = StringIO.StringIO("bplist00") plist = binplist.BinaryPlist(usual_header) plist._ReadHeader() self.assertEqual(plist.version, "00")
def _OpenPlistFile(self, searcher, path_spec): """Open a Plist file given a path and returns a plist top level object. Args: searcher: The file system searcher object (instance of dfvfs.FileSystemSearcher). path_spec: The path specification (instance of dfvfs.PathSpec) of the plist file. Raises: errors.PreProcessFail: if the preprocessing fails. """ plist_file_location = getattr(path_spec, 'location', u'') file_entry = searcher.GetFileEntryByPathSpec(path_spec) file_object = file_entry.GetFileObject() try: plist_file = binplist.BinaryPlist(file_object) top_level_object = plist_file.Parse() except binplist.FormatError as exception: exception = utils.GetUnicodeString(exception) raise errors.PreProcessFail( u'File is not a plist: {0:s}'.format(exception)) except OverflowError as exception: raise errors.PreProcessFail( u'Error processing: {0:s} with error: {1:s}'.format( plist_file_location, exception)) if not plist_file: raise errors.PreProcessFail( u'File is not a plist: {0:s}'.format(plist_file_location)) return top_level_object
def __init__(): f = open(sys.argv[1], 'rb') plist = binplist.BinaryPlist(f, False, False) try: parsed_plist = plist.Parse() except binplist.FormatError: print "Error!" exit() ''' system_items = parsed_plist['systemitems'] elem_list = system_items['VolumesList'] favorite_items = parsed_plist['favorites'] elem_list.extend(favorite_items['VolumesList']) ''' favorite_items = parsed_plist['favorites'] elem_list = favorite_items['VolumesList'] for volume in elem_list: if 'Alias' in volume: try: data = volume['Alias'] s = s_alias.parse(data) data = data[s_alias.sizeof():] # Search for 0x000e volume ID type = s_type.parse(data) data = data[s_type.sizeof():] while type != 14 and data != '': type = s_type.parse(data) data = data[s_type.sizeof():] except: print "Fail!" continue # If not volume ID if data == '': print "Fail!" continue v = s_volume.parse(data) time = datetime.datetime.fromtimestamp( s.timestamp1 - HFS_to_Epoch).strftime('%Y-%m-%d %H:%M:%S') print u'\n\tFile name: {}'.format(v.volume1) print u'\tVolume name: {}'.format(v.volume2) print u'\tTime: {}'.format(time) if s.timestamp1 != s.timestamp2: time = datetime.datetime.fromtimestamp( s.timestamp2 - HFS_to_Epoch).strftime('%Y-%m-%d %H:%M:%S') print u'\tSecond time: {}'.format(time) type = s_type.parse(data) data = data[s_type.sizeof():] while type != 19 and data != '': type = s_type.parse(data) data = data[s_type.sizeof():] if data == '': continue mount_point = s_mount_point.parse(data) print u'\tMount point: {}'.format(mount_point)
def testParseOfftable(self): plist = binplist.BinaryPlist(self.minimal) # Start by parsing a minimal offsets table plist._ReadTrailer() self.assertRaises(binplist.FormatError, plist._ReadOffsetTable) # single offset table plist = binplist.BinaryPlist(self.single) plist._ReadTrailer() plist._ReadOffsetTable() self.assertListEqual([0x09], plist.object_offsets) # Test the plist that doesn't have a full trailer plist = binplist.BinaryPlist(self.short) plist._ReadTrailer() plist._ReadOffsetTable() self.assertListEqual([9, 10, 11], plist.object_offsets) # Test the plist with an offset table that overflows the file plist = binplist.BinaryPlist(self.overflow) plist._ReadTrailer() self.assertRaises(binplist.FormatError, plist._ReadOffsetTable)
def parse_plist(fd): bplist = binplist.BinaryPlist(fd) parsed_list = bplist.Parse() for item in parsed_list['$objects']: if isinstance(item, (str)): if 'file:///' in item: try: print str(item).encode('utf-8') except: pass
def testCanParseBinplistAtOffset(self): for _ in range(5): self.single.seek(0, os.SEEK_SET) rand_int = random.randint(0, 2048) padding = "A" * rand_int padded_singleplist = padding + self.single.read() fd = StringIO.StringIO(padded_singleplist) fd.seek(rand_int, os.SEEK_SET) plist = binplist.BinaryPlist(fd) plist.Parse()
def ObjectTest(self, values): for test_value in values: raises, data, expected_result, result_type = test_value fd = StringIO.StringIO(data) plist = binplist.BinaryPlist(fd) if not raises: result = plist._ParseObject() logging.debug("Result was %r.", result) self.assertTrue(isinstance(result, result_type)) self.assertEqual(result, expected_result) else: self.assertRaises(expected_result, plist._ParseObject)
def get_parrot_zik_mac_darwin(): fd = open("/Library/Preferences/com.apple.Bluetooth.plist", "rb") plist = binplist.BinaryPlist(file_obj=fd) parsed_plist = plist.Parse() try: for mac in parsed_plist['PairedDevices']: if p.match(mac.replace("-", ":")): return mac.replace("-", ":") else: raise DeviceNotConnected except Exception: pass
def test_ParseObjectByIndex(self): unicode_string = ( "\x6F\x00\x11\x65\xaf\x8b\xfa\x76\x7b\x90\x7f\x96\xbe" "\x75\x33\x8b\xf7\x7e\xc8\x88\xab\x90\x1a\x8f\xc7\x00" "\x20\x00\x2d\x00\x20\x00\x68\x00\x65\x00\x68") actual_string = u"斯诺登避难申请终被通过 - heh" fd = StringIO.StringIO(unicode_string) plist = binplist.BinaryPlist(file_obj=fd) # Make an external offset_list offset_list = [0] resulting_object = plist._ParseObjectByIndex(0, offset_list) self.assertEqual(resulting_object, actual_string) # Second test second_resulting_object = plist._ParseObjectByIndex(0, offset_list) # Test that the UTF16 object was in the cache self.assert_(second_resulting_object is resulting_object) self.assertEqual(resulting_object, actual_string) # Now test for something more complex. An array that has both unicode and # str strings. two_element_array = ( "\xA2" # Array of 2 objects "\x01" # Reference to object unicode string "\x02" # Reference to object str string "\x61\x65\xaf" # Unicode object "\x52\x99\xcd" # Str object ) fd = StringIO.StringIO(two_element_array) plist = binplist.BinaryPlist(fd) # Parsing with _ParseObjectByIndex requires setting up some bplist # properties. offset_list = [0, 3, 6] plist.object_count = len(offset_list) plist.object_offsets = offset_list plist.object_ref_size = 1 plist._file_size = len(two_element_array) resulting_object = plist._ParseObjectByIndex(0, offset_list) self.assertEqual(resulting_object, [u"斯", "\x99\xcd"])
def testParseBoolFill(self): # null data = StringIO.StringIO("\x00") plist = binplist.BinaryPlist(data) self.assertEqual(binplist.NullValue, plist._ParseObject()) # false data = StringIO.StringIO("\x08") plist = binplist.BinaryPlist(data) self.assertEqual(False, plist._ParseObject()) # true data = StringIO.StringIO("\x09") plist = binplist.BinaryPlist(data) self.assertEqual(True, plist._ParseObject()) # fill byte data = StringIO.StringIO("\x0F") plist = binplist.BinaryPlist(data) self.assertEqual(None, plist._ParseObject()) # unknown for i in "\x01\x02\x03\x04\x05\x06\x07\x0a\x0b\x0c\x0d\x0e": unk = StringIO.StringIO(i) plist = binplist.BinaryPlist(unk) self.assertEqual(binplist.UnknownObject, plist._ParseObject())
def ParseFile(self, file_entry, file_object): """Parses the plist file and returns the parsed key. Args: file_entry: The file entry (instance of dfvfs.FileEntry). file_object: The file-like object. Returns: The value of the first key defined by PLIST_KEYS that is found. Raises: errors.PreProcessFail: if the preprocessing fails. """ try: plist_file = binplist.BinaryPlist(file_object) top_level_object = plist_file.Parse() except binplist.FormatError as exception: raise errors.PreProcessFail( u'File is not a plist: {0:s} with error: {1:s}'.format( file_entry.path_spec.comparable, exception)) except OverflowError as exception: raise errors.PreProcessFail( u'Unable to process plist: {0:s} with error: {1:s}'.format( file_entry.path_spec.comparable, exception)) if not plist_file: raise errors.PreProcessFail(u'File is not a plist: {0:s}'.format( file_entry.path_spec.comparable)) match = None key_name = '' for plist_key in self.PLIST_KEYS: try: match = plist_interface.GetKeys(top_level_object, frozenset([plist_key])) except KeyError: continue if match: key_name = plist_key break if not match: raise errors.PreProcessFail( u'Keys not found inside plist file: {0:s}.'.format(u','.join( self.PLIST_KEYS))) return self.ParseKey(match, key_name)
def GetEntries(self, parser_mediator, match=None, **unused_kwargs): """Extracts relevant user timestamp entries. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. match (Optional[dict[str: object]]): keys extracted from PLIST_KEYS. """ if u'name' not in match or u'uid' not in match: return account = match[u'name'][0] uid = match[u'uid'][0] # INFO: binplist return a string with the Plist XML. for policy in match.get(u'passwordpolicyoptions', []): try: xml_policy = ElementTree.fromstring(policy) except (ElementTree.ParseError, LookupError) as exception: logging.error(( u'Unable to parse XML structure for an user policy, account: ' u'{0:s} and uid: {1!s}, with error: {2:s}').format( account, uid, exception)) continue for dict_elements in xml_policy.iterfind(u'dict'): key_values = [ value.text for value in dict_elements.getchildren() ] # Taking a list and converting it to a dict, using every other item # as the key and the other one as the value. policy_dict = dict(zip(key_values[0::2], key_values[1::2])) time_string = policy_dict.get(u'passwordLastSetTime', None) if time_string and time_string != u'2001-01-01T00:00:00Z': try: date_time = dfdatetime_time_elements.TimeElements() date_time.CopyFromStringISO8601(time_string) except ValueError: date_time = None parser_mediator.ProduceExtractionError( u'unable to parse passworkd last set time string: {0:s}' .format(time_string)) shadow_hash_data = match.get(u'ShadowHashData', None) if date_time and isinstance(shadow_hash_data, (list, tuple)): # Extract the hash password information. # It is store in the attribute ShadowHasData which is # a binary plist data; However binplist only extract one # level of binary plist, then it returns this information # as a string. # TODO: change this into a DataRange instead. For this we # need the file offset and size of the ShadowHashData value data. shadow_hash_data = shadow_hash_data[0] resolver_context = context.Context() fake_file = fake_file_io.FakeFile(resolver_context, shadow_hash_data) shadow_hash_data_path_spec = fake_path_spec.FakePathSpec( location=u'ShadowHashData') fake_file.open(path_spec=shadow_hash_data_path_spec) try: plist_file = binplist.BinaryPlist(file_obj=fake_file) top_level = plist_file.Parse() except binplist.FormatError: top_level = dict() salted_hash = top_level.get(u'SALTED-SHA512-PBKDF2', None) if salted_hash: password_hash = u'$ml${0:d}${1:s}${2:s}'.format( salted_hash[u'iterations'], binascii.hexlify(salted_hash[u'salt']), binascii.hexlify(salted_hash[u'entropy'])) else: password_hash = u'N/A' event_data = plist_event.PlistTimeEventData() event_data.desc = ( u'Last time {0:s} ({1!s}) changed the password: {2!s}' ).format(account, uid, password_hash) event_data.key = u'passwordLastSetTime' event_data.root = self._ROOT event = time_events.DateTimeValuesEvent( date_time, definitions.TIME_DESCRIPTION_WRITTEN) parser_mediator.ProduceEventWithEventData( event, event_data) time_string = policy_dict.get(u'lastLoginTimestamp', None) if time_string and time_string != u'2001-01-01T00:00:00Z': try: date_time = dfdatetime_time_elements.TimeElements() date_time.CopyFromStringISO8601(time_string) except ValueError: date_time = None parser_mediator.ProduceExtractionError( u'unable to parse last login time string: {0:s}'. format(time_string)) if date_time: event_data = plist_event.PlistTimeEventData() event_data.desc = u'Last login from {0:s} ({1!s})'.format( account, uid) event_data.key = u'lastLoginTimestamp' event_data.root = self._ROOT event = time_events.DateTimeValuesEvent( date_time, definitions.TIME_DESCRIPTION_WRITTEN) parser_mediator.ProduceEventWithEventData( event, event_data) time_string = policy_dict.get(u'failedLoginTimestamp', None) if time_string and time_string != u'2001-01-01T00:00:00Z': try: date_time = dfdatetime_time_elements.TimeElements() date_time.CopyFromStringISO8601(time_string) except ValueError: date_time = None parser_mediator.ProduceExtractionError( u'unable to parse failed login time string: {0:s}'. format(time_string)) if date_time: event_data = plist_event.PlistTimeEventData() event_data.desc = ( u'Last failed login from {0:s} ({1!s}) ({2!s} times)' ).format(account, uid, policy_dict.get(u'failedLoginCount', 0)) event_data.key = u'failedLoginTimestamp' event_data.root = self._ROOT event = time_events.DateTimeValuesEvent( date_time, definitions.TIME_DESCRIPTION_WRITTEN) parser_mediator.ProduceEventWithEventData( event, event_data)
def GetEntries(self, match, **unused_kwargs): """Extracts relevant user timestamp entries. Args: match: A dictionary containing keys extracted from PLIST_KEYS. Yields: EventObject objects extracted from the plist. """ root = '/' account = match['name'][0] uid = match['uid'][0] cocoa_zero = ( timelib.Timestamp.COCOA_TIME_TO_POSIX_BASE * timelib.Timestamp.MICRO_SECONDS_PER_SECOND) # INFO: binplist return a string with the Plist XML. for policy in match['passwordpolicyoptions']: xml_policy = ElementTree.fromstring(policy) for dict_elements in xml_policy.iterfind('dict'): key_values = [value.text for value in dict_elements.getchildren()] policy_dict = dict(zip(key_values[0::2], key_values[1::2])) if policy_dict.get('passwordLastSetTime', 0): timestamp = timelib.Timestamp.FromTimeString( policy_dict.get('passwordLastSetTime', '0')) if timestamp > cocoa_zero: # Extract the hash password information. # It is store in the attribure ShadowHasData which is # a binary plist data; However binplist only extract one # level of binary plist, then it returns this information # as a string. fake_file = interface.FakeFile(match['ShadowHashData'][0]) try: plist_file = binplist.BinaryPlist(file_obj=fake_file) top_level = plist_file.Parse() except binplist.FormatError: top_level = dict() salted_hash = top_level.get('SALTED-SHA512-PBKDF2', None) if salted_hash: password_hash = u'$ml${}${}${}'.format( salted_hash['iterations'], binascii.hexlify(salted_hash['salt']), binascii.hexlify(salted_hash['entropy'])) else: password_hash = u'N/A' description = u'Last time {} ({}) changed the password: {}'.format( account, uid, password_hash) yield plist_event.PlistTimeEvent( root, u'passwordLastSetTime', timestamp, description) if policy_dict.get('lastLoginTimestamp', 0): timestamp = timelib.Timestamp.FromTimeString( policy_dict.get('lastLoginTimestamp', '0')) description = u'Last login from {} ({})'.format( account, uid) if timestamp > cocoa_zero: yield plist_event.PlistTimeEvent( root, u'lastLoginTimestamp', timestamp, description) if policy_dict.get('failedLoginTimestamp', 0): timestamp = timelib.Timestamp.FromTimeString( policy_dict.get('failedLoginTimestamp', '0')) description = u'Last failed login from {} ({}) ({} times)'.format( account, uid, policy_dict['failedLoginCount']) if timestamp > cocoa_zero: yield plist_event.PlistTimeEvent( root, u'failedLoginTimestamp', timestamp, description)
value.append(INT.parse(bookmark)) bookmark = bookmark[4:] if token.type == 1024: t = value.pop(0) + 978307200 timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t)) print u'Timestamp: {}, extra: {}'.format(timestamp, value) else: print u'Unknown type {}({}): {}'.format( token.type, hex(token.type), value) print "--------///-------" name = sys.argv[1] fd = open(name, 'rb') plist = binplist.BinaryPlist(fd, False, False) print u'File: {}\n'.format(name) try: parsed_plist = plist.Parse() except binplist.FormatError, e: parsed_plist = plistlib.readPlist(name) # /Users/moxilo/Library/Preferences # Recent documents # com.apple.PROGRAM.LSSharedFileList.plist # com.apple.recentitems.plist try: documents = parsed_plist['RecentDocuments'] except KeyError:
data_to_return = self._data[self._offset:] self._offset = len(self._data) return data_to_return data_to_return = self._data[self._offset:self._offset + size] self._offset += size return data_to_return def close(self): pass name = sys.argv[1] fd = open(name, 'rb') plist = binplist.BinaryPlist(fd, False, False) parsed_plist = plist.Parse() account = parsed_plist['name'][0] name = parsed_plist['realname'][0] uid = parsed_plist['uid'][0] gid = parsed_plist['gid'][0] shell = parsed_plist['shell'][0] password = parsed_plist['authentication_authority'] ShadowHashData = parsed_plist['ShadowHashData'] foo = FooFile(ShadowHashData[0]) plist_file = binplist.BinaryPlist(file_obj=foo) top_level = plist_file.Parse()['SALTED-SHA512-PBKDF2'] salt = binascii.hexlify(top_level['salt']) entropy = binascii.hexlify(top_level['entropy']) iterations = top_level['iterations']
def testParseInt(self): # 1 byte data = StringIO.StringIO("\x10\x00") plist = binplist.BinaryPlist(data) self.assertEqual(0, plist._ParseObject()) # 2 bytes data = StringIO.StringIO("\x11\x00\x01") plist = binplist.BinaryPlist(data) self.assertEqual(1, plist._ParseObject()) data = StringIO.StringIO("\x11\x01\x00") plist = binplist.BinaryPlist(data) self.assertEqual(256, plist._ParseObject()) # 4 bytes data = StringIO.StringIO("\x12\x00\x00\x00\x01") plist = binplist.BinaryPlist(data) self.assertEqual(1, plist._ParseObject()) # 8 bytes - should be unsigned data = StringIO.StringIO("\x13\x00\x00\x00\x00\x00\x00\x00\x01") plist = binplist.BinaryPlist(data) self.assertEqual(1, plist._ParseObject()) # Now with version 00 - signed data = StringIO.StringIO("\x13\x00\x00\x00\x00\x00\x00\x00\x01") plist = binplist.BinaryPlist(data) plist.version = "00" self.assertEqual(1, plist._ParseObject()) # 8 bytes - should be unsigned data = StringIO.StringIO("\x13\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE") plist = binplist.BinaryPlist(data) self.assertEqual((1 << 64) - 2, plist._ParseObject()) # Now with version 00 - signed data = StringIO.StringIO("\x13\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE") plist = binplist.BinaryPlist(data) plist.version = "00" self.assertEqual(-2, plist._ParseObject()) # 16 bytes - should be unsigned raw = "\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" data = StringIO.StringIO(raw) plist = binplist.BinaryPlist(data) self.assertEqual(1, plist._ParseObject()) raw = "\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" data = StringIO.StringIO(raw) plist = binplist.BinaryPlist(data) plist.version = "00" self.assertEqual(1, plist._ParseObject()) # 16 bytes - should be unsigned raw = "\x14\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE" data = StringIO.StringIO(raw) plist = binplist.BinaryPlist(data) self.assertEqual((1 << 128) - 2, plist._ParseObject()) # Now with version 00 - signed raw = "\x14\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE" data = StringIO.StringIO(raw) plist = binplist.BinaryPlist(data) plist.version = "00" self.assertEqual(-2, plist._ParseObject()) # Test incomplete data data = StringIO.StringIO("\x10") plist = binplist.BinaryPlist(data) int_object = plist._ParseObject() self.assertTrue(isinstance(int_object, binplist.RawValue)) data = StringIO.StringIO("\x11\x12") plist = binplist.BinaryPlist(data) int_object = plist._ParseObject() self.assertTrue(isinstance(int_object, binplist.RawValue)) # Test unknown size data = StringIO.StringIO("\x16\x00\x00") plist = binplist.BinaryPlist(data) int_object = plist._ParseObject() self.assertEqual(int_object, binplist.RawValue("\x00\x00"))
def GetEntries(self, parser_context, match=None, **unused_kwargs): """Extracts relevant user timestamp entries. Args: parser_context: A parser context object (instance of ParserContext). match: Optional dictionary containing keys extracted from PLIST_KEYS. The default is None. """ account = match['name'][0] uid = match['uid'][0] cocoa_zero = ( timelib.Timestamp.COCOA_TIME_TO_POSIX_BASE * timelib.Timestamp.MICRO_SECONDS_PER_SECOND) # INFO: binplist return a string with the Plist XML. for policy in match['passwordpolicyoptions']: xml_policy = ElementTree.fromstring(policy) for dict_elements in xml_policy.iterfind('dict'): key_values = [value.text for value in dict_elements.getchildren()] policy_dict = dict(zip(key_values[0::2], key_values[1::2])) if policy_dict.get('passwordLastSetTime', 0): timestamp = timelib.Timestamp.FromTimeString( policy_dict.get('passwordLastSetTime', '0')) if timestamp > cocoa_zero: # Extract the hash password information. # It is store in the attribure ShadowHasData which is # a binary plist data; However binplist only extract one # level of binary plist, then it returns this information # as a string. # TODO: change this into a DataRange instead. For this we # need the file offset and size of the ShadowHashData value data. resolver_context = context.Context() fake_file = fake_file_io.FakeFile( resolver_context, match['ShadowHashData'][0]) fake_file.open(path_spec=fake_path_spec.FakePathSpec( location=u'ShadowHashData')) try: plist_file = binplist.BinaryPlist(file_obj=fake_file) top_level = plist_file.Parse() except binplist.FormatError: top_level = dict() salted_hash = top_level.get('SALTED-SHA512-PBKDF2', None) if salted_hash: password_hash = u'$ml${0:d}${1:s}${2:s}'.format( salted_hash['iterations'], binascii.hexlify(salted_hash['salt']), binascii.hexlify(salted_hash['entropy'])) else: password_hash = u'N/A' description = ( u'Last time {0:s} ({1!s}) changed the password: {2!s}').format( account, uid, password_hash) event_object = plist_event.PlistTimeEvent( self._ROOT, u'passwordLastSetTime', timestamp, description) parser_context.ProduceEvent(event_object, plugin_name=self.NAME) if policy_dict.get('lastLoginTimestamp', 0): timestamp = timelib.Timestamp.FromTimeString( policy_dict.get('lastLoginTimestamp', '0')) description = u'Last login from {0:s} ({1!s})'.format(account, uid) if timestamp > cocoa_zero: event_object = plist_event.PlistTimeEvent( self._ROOT, u'lastLoginTimestamp', timestamp, description) parser_context.ProduceEvent(event_object, plugin_name=self.NAME) if policy_dict.get('failedLoginTimestamp', 0): timestamp = timelib.Timestamp.FromTimeString( policy_dict.get('failedLoginTimestamp', '0')) description = ( u'Last failed login from {0:s} ({1!s}) ({2!s} times)').format( account, uid, policy_dict['failedLoginCount']) if timestamp > cocoa_zero: event_object = plist_event.PlistTimeEvent( self._ROOT, u'failedLoginTimestamp', timestamp, description) parser_context.ProduceEvent(event_object, plugin_name=self.NAME)
options = parser.parse_args() if not options.plist: parser.print_help() sys.exit(-1) ultra_verbosity = False if options.verbose: if len(options.verbose) == 1: logging.basicConfig(level=logging.DEBUG) else: ultra_verbosity = True logging.basicConfig(level=binplist.LOG_ULTRA_VERBOSE) with open(options.plist, "rb") as fd: plist = binplist.BinaryPlist(file_obj=fd, ultra_verbosity=ultra_verbosity, discovery_mode=options.discovery_mode) try: parsed_plist = plist.Parse() if plist.is_corrupt: logging.warn( "%s LOOKS CORRUPTED. You might not obtain all data!\n", options.plist) except binplist.FormatError, e: parsed_plist = plistlib.readPlist(options.plist) print binplist.PlistToUnicode( parsed_plist, string_encoding=options.string_encoding, encoding_options=options.string_encoding_option).encode( options.output_encoding, options.output_encoding_option)
def testParseArray(self): values = [ # (ref_size, object_offsets, expected_result, data) # Array of 0 objects (1, [], [], "\xA0"), # Array of 1 object ( 1, [0, 2], [False], ( "\xA1" # Array of 1 object "\x01" # Reference to object False "\x08" # False object )), # Array of 2 objects ( 1, [0, 3, 4], [False, True], ( "\xA2" # Array of 2 objects "\x01" # Reference to object False "\x02" # Reference to object True "\x08" # False object "\x09" # True object )), # Array of 2 objects, 1 nonexistant ( 1, [0, 3, 4], [False, binplist.CorruptReference], ( "\xA2" # Array of 2 objects "\x01" # Reference to object False "\x09" # Reference to a nonexistant object "\x08" # False object "\x09" # True object )), # Array of 2 objects, 1 out of bounds ( 1, [0, 3, 200], [False, binplist.CorruptReference], ( "\xA2" # Array of 2 objects "\x01" # Reference to object False "\x02" # Reference out of bounds "\x08" # False object "\x09" # True object )), # Array of 2 objects, 1 circular reference to the array itself ( 1, [0, 3, 4], [False, binplist.CorruptReference], ( "\xA2" # Array of 2 objects "\x01" # Reference to object False "\x00" # circular reference to the array "\x08" # False object "\x09" # True object )), # Array of 2 objects, one a False value. The other is an array that # has a reference to the first array. # Tests deep circular reference detection ( 1, [0, 3, 4], [False, [binplist.CorruptReference]], ( "\xA2" # Array of 2 objects "\x01" # Reference to object False "\x02" # Reference to the second array "\x08" # False object "\xA1" # Array of 1 object, points to the first array "\x00" # circular reference to the first array )), # Array with not enough elements. This is hardly possible # in a real world scenario because of the trailer always being # past the objects. However on a corrupt bplist the object offset might # be pointing to the last elements of the trailer, one of them # being interpreted as an array... Thus why this scenario. ( 1, [0, 3, 4], [binplist.CorruptReference, binplist.CorruptReference], ( "\xA2" # Array of 2 objects "\x01" # Reference to a nonexistant object )), ] for value in values: (ref_size, object_offsets, expected_result, data) = value fd = StringIO.StringIO(data) plist = binplist.BinaryPlist(fd) # Fill objects_traversed with the current value as if we had been called # by a normal _Parse plist.objects_traversed = {0} plist.object_ref_size = ref_size plist.object_offsets = object_offsets plist.object_count = len(object_offsets) result = plist._ParseObject() self.assertListEqual(expected_result, result) # Test that the circular reference detection helper is cleaned properly self.assertSetEqual(plist.objects_traversed, {0})
def GetEntries(self, parser_mediator, match=None, **unused_kwargs): """Extracts relevant user timestamp entries. Args: parser_mediator: A parser mediator object (instance of ParserMediator). match: Optional dictionary containing keys extracted from PLIST_KEYS. The default is None. """ if u'name' not in match or u'uid' not in match: return account = match[u'name'][0] uid = match[u'uid'][0] cocoa_zero = (timelib.Timestamp.COCOA_TIME_TO_POSIX_BASE * timelib.Timestamp.MICRO_SECONDS_PER_SECOND) # INFO: binplist return a string with the Plist XML. for policy in match.get(u'passwordpolicyoptions', []): try: xml_policy = ElementTree.fromstring(policy) except (ElementTree.ParseError, LookupError) as exception: logging.error(( u'Unable to parse XML structure for an user policy, account: ' u'{0:s} and uid: {1!s}, with error: {2:s}').format( account, uid, exception)) continue for dict_elements in xml_policy.iterfind(u'dict'): key_values = [ value.text for value in dict_elements.getchildren() ] # Taking a list and converting it to a dict, using every other item # as the key and the other one as the value. policy_dict = dict(zip(key_values[0::2], key_values[1::2])) time_string = policy_dict.get(u'passwordLastSetTime', None) if time_string: try: timestamp = timelib.Timestamp.FromTimeString(time_string) except errors.TimestampError: parser_mediator.ProduceParseError( u'Unable to parse time string: {0:s}'.format( time_string)) timestamp = 0 shadow_hash_data = match.get(u'ShadowHashData', None) if timestamp > cocoa_zero and isinstance( shadow_hash_data, (list, tuple)): # Extract the hash password information. # It is store in the attribute ShadowHasData which is # a binary plist data; However binplist only extract one # level of binary plist, then it returns this information # as a string. # TODO: change this into a DataRange instead. For this we # need the file offset and size of the ShadowHashData value data. shadow_hash_data = shadow_hash_data[0] resolver_context = context.Context() fake_file = fake_file_io.FakeFile(resolver_context, shadow_hash_data) fake_file.open(path_spec=fake_path_spec.FakePathSpec( location=u'ShadowHashData')) try: plist_file = binplist.BinaryPlist(file_obj=fake_file) top_level = plist_file.Parse() except binplist.FormatError: top_level = dict() salted_hash = top_level.get(u'SALTED-SHA512-PBKDF2', None) if salted_hash: password_hash = u'$ml${0:d}${1:s}${2:s}'.format( salted_hash[u'iterations'], binascii.hexlify(salted_hash[u'salt']), binascii.hexlify(salted_hash[u'entropy'])) else: password_hash = u'N/A' description = ( u'Last time {0:s} ({1!s}) changed the password: {2!s}' ).format(account, uid, password_hash) event_object = plist_event.PlistTimeEvent( self._ROOT, u'passwordLastSetTime', timestamp, description) parser_mediator.ProduceEvent(event_object) time_string = policy_dict.get(u'lastLoginTimestamp', None) if time_string: try: timestamp = timelib.Timestamp.FromTimeString(time_string) except errors.TimestampError: parser_mediator.ProduceParseError( u'Unable to parse time string: {0:s}'.format( time_string)) timestamp = 0 description = u'Last login from {0:s} ({1!s})'.format( account, uid) if timestamp > cocoa_zero: event_object = plist_event.PlistTimeEvent( self._ROOT, u'lastLoginTimestamp', timestamp, description) parser_mediator.ProduceEvent(event_object) time_string = policy_dict.get(u'failedLoginTimestamp', None) if time_string: try: timestamp = timelib.Timestamp.FromTimeString(time_string) except errors.TimestampError: parser_mediator.ProduceParseError( u'Unable to parse time string: {0:s}'.format( time_string)) timestamp = 0 description = ( u'Last failed login from {0:s} ({1!s}) ({2!s} times)' ).format(account, uid, policy_dict.get(u'failedLoginCount', 0)) if timestamp > cocoa_zero: event_object = plist_event.PlistTimeEvent( self._ROOT, u'failedLoginTimestamp', timestamp, description) parser_mediator.ProduceEvent(event_object)
def testParseDict(self): values = [ # (ref_size, object_offsets, expected_result, data) # Dict of 0 objects (1, [], {}, "\xD0"), # Dict of 1 entry ( 1, [0, 3, 5], { "a": True }, ( "\xD1" # Dict of 1 entry "\x01" # Ref to key#1 "\x02" # Ref to val#1 "\x51a" # "a" (key#1) "\x09" # True (val#1) )), # Dict of 1 entry, a key being an integer ( 1, [0, 3, 5], { 1: True }, ( "\xD1" # Dict of 1 entry "\x01" # Ref to key#1 "\x02" # Ref to val#1 "\x10\x01" # 1 (key#1) "\x09" # True (val#1) )), # Dict of 1 entry, has a circular key ( 1, [0, 3, 5], { "corrupt:0": True }, ( "\xD1" # Dict of 1 entry "\x00" # Circular key "\x02" # Ref to val#1 "\x10\x01" # 1 (key#1) "\x09" # True (val#1) )), # Dict of 1 entry, has a circular value ( 1, [0, 3, 5], { 1: binplist.CorruptReference }, ( "\xD1" # Dict of 1 entry "\x01" # Ref to key#1 "\x00" # Circular value "\x10\x01" # 1 (key#1) "\x09" # True (val#1) )), # Dict of 1 entry, has both a circular key and a circular value ( 1, [0, 3, 5], { "corrupt:0": binplist.CorruptReference }, ( "\xD1" # Dict of 1 entry "\x00" # Circular key "\x00" # Circular value "\x10\x01" # 1 (key#1) "\x09" # True (val#1) )), # Dict of 1 entry, value is a list that contains a circular value ( 1, [0, 3, 5, 8], { "a": [binplist.CorruptReference, 1] }, ( "\xD1" # Dict of 1 entry "\x01" # key#1 "\x02" # val#1 "\x51a" # "a" (key#1) "\xA2\x00\x03" # Array with 2 elements. The dict and an integer "\x10\x01" # 1 )), # Dict of 2 entries ( 1, [0, 5, 7, 9, 10], { "a": False, "b": True }, ( "\xD2" # Dict of 2 entries "\x01" # key#1 "\x02" # key#2 "\x03" # val#2 "\x04" # val#2 "\x51a" # "a" "\x51b" # "b" "\x08" # False object "\x09" # True object )), # Dict with not enough references ( 1, [0], { "corrupt:1": binplist.CorruptReference }, ( "\xD1" # Dict of 1 entry "\x01" # key#1 )), # Dict with a nonexistant reference ( 1, [0], { "corrupt:32": binplist.CorruptReference }, ( "\xD1" # Dict of 1 entry "\x20" # key#1 "\x99" # val#1 )), ] for value in values: (ref_size, object_offsets, expected_result, data) = value fd = StringIO.StringIO(data) plist = binplist.BinaryPlist(fd) # Fill objects_traversed with the current value as if we had been called # by a normal _Parse plist.objects_traversed = {0} plist.object_ref_size = ref_size plist.object_offsets = object_offsets plist.object_count = len(object_offsets) result = plist._ParseObject() self.assertEqual(expected_result, result) # Test that the circular reference detection helper is cleaned properly self.assertSetEqual(plist.objects_traversed, {0})