def decrypt_Metadata(item, df_meta): if item['keyclass'] >=6 : bplist = BytesIO(item['encryptedMetadata_wrappedKey']) plist = ccl_bplist.load(bplist) metaDataWrappedKeyDeserialized = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) authCode = metaDataWrappedKeyDeserialized['root']['SFAuthenticationCode'] iv = metaDataWrappedKeyDeserialized['root']['SFInitializationVector'] ciphertext = metaDataWrappedKeyDeserialized['root']['SFCiphertext'] unwrapped_metadata_key = unwrap_key( df_meta[df_meta.keyclass == int(item['keyclass'])].iloc[0].data, item['keyclass'] ) gcm = AES.new(unhexlify(unwrapped_metadata_key)[:32], AES.MODE_GCM, iv) metadata_key = gcm.decrypt_and_verify(ciphertext, authCode) bplist = BytesIO(item['encryptedMetadata_ciphertext']) plist = ccl_bplist.load(bplist) metaDataDeserialized = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) authCode = metaDataDeserialized['root']['SFAuthenticationCode'] iv = metaDataDeserialized['root']['SFInitializationVector'] ciphertext = metaDataDeserialized['root']['SFCiphertext'] gcm = AES.new(metadata_key[:32], AES.MODE_GCM, iv) decrypted = gcm.decrypt_and_verify(ciphertext, authCode) der_data = decode(decrypted)[0] item['decrypted'] = {} for k in der_data: if 'Octet' in str(type(k[1])): item['decrypted'][str(k[0])] = bytes(k[1]) else: item['decrypted'][str(k[0])] = str(k[1]) return item
def ParseSFL2(MRUFile): itemsLinkList = [] try: plistfile = open(MRUFile, "rb") plist = ccl_bplist.load(plistfile) plistfile.close() plist_objects = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) if plist_objects["root"]["NS.keys"][0] == "items": items = plist_objects["root"]["NS.objects"][0]["NS.objects"] for n, item in enumerate(items): attribute_keys = plist_objects["root"]["NS.objects"][0][ "NS.objects"][n]["NS.keys"] attribute_values = plist_objects["root"]["NS.objects"][0][ "NS.objects"][n]["NS.objects"] attributes = dict(zip(attribute_keys, attribute_values)) if "Bookmark" in attributes: if isinstance(attributes["Bookmark"], str): itemLink = BLOBParser_human(attributes["Bookmark"]) else: itemLink = BLOBParser_human( attributes["Bookmark"]['NS.data']) itemsLinkList.append(itemLink) return itemsLinkList except Exception as e: print e
def ParseSFL(MRUFile): plistfile = open(MRUFile, "rb") plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) if plist_objects["root"]["NS.objects"][1]["NS.keys"][0] == "com.apple.LSSharedFileList.MaxAmount": numberOfItems = plist_objects["root"]["NS.objects"][1]["NS.objects"][0] print "Max number of recent items in this plist: " + str(numberOfItems) if plist_objects["root"]["NS.keys"][2] == "items": items = plist_objects["root"]["NS.objects"][2]["NS.objects"] for n,item in enumerate(items): print" [Item Number: " + str(n) + " | Order: " + str(item["order"]) + "] Name:'" + item["name"] + "' (URL:'" + item["URL"]['NS.relative'] + "'')" #UNCOMMENT FOR UNIQUE IDENTIFIER HEXDUMP #print "----------------------------------------------------------------------------" #print "Hexdump of Unique Identifier: " #print hexdump.hexdump(item["uniqueIdentifier"]["NS.uuidbytes"]) #print "----------------------------------------------------------------------------" if args.blob == True: print "----------------------------------------------------------------------------" print "Hexdump of Bookmark BLOB: " hexdump_blob = hexdump.hexdump(item["bookmark"]) print hexdump_blob print "----------------------------------------------------------------------------"
def get_login_items_entries(btm_file): with open(btm_file, 'rb') as fp: plist = ccl_bplist.load(fp) ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) ccl_bplist.set_object_converter( ccl_bplist.NSKeyedArchiver_common_objects_convertor) return ns_keyed_archiver_obj['root']['backgroundItems'][ 'allContainers']
def ParseSFL2_FavoriteVolumes(MRUFile): try: plistfile = open(MRUFile, "rb") plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) print "Item number has no bearing upon time of usage." print "Plist Properties:" if plist_objects["root"]["NS.keys"][1] == "properties": properties_keys = plist_objects["root"]["NS.objects"][1]["NS.keys"] properties_values = plist_objects["root"]["NS.objects"][1]["NS.objects"] properties = dict(zip(properties_keys,properties_values)) for key in properties: print " " + key + ": " + str(properties[key]) if plist_objects["root"]["NS.keys"][0] == "items": items = plist_objects["root"]["NS.objects"][0]["NS.objects"] for n,item in enumerate(items): attribute_keys = plist_objects["root"]["NS.objects"][0]["NS.objects"][n]["NS.keys"] attribute_values = plist_objects["root"]["NS.objects"][0]["NS.objects"][n]["NS.objects"] attributes = dict(zip(attribute_keys,attribute_values)) try: uuid = attributes["uuid"] except: uuid = "No 'UUID' Attribute" try: visability = str(attributes["visibility"]) except: visability = "No 'Visability' Attribute" try: name = attributes["Name"] except: name = "No 'Name' Attribute (Use BLOB parser for name)" print "\n [Item Number: " + str(n) + " | (UUID:'" + uuid + "') | Visibility: " + visability + "] Name: '" + name + "'" if attributes["CustomItemProperties"]: CIP_keys = plist_objects["root"]["NS.objects"][0]["NS.objects"][n]["NS.objects"][1]["NS.keys"] CIP_values = plist_objects["root"]["NS.objects"][0]["NS.objects"][n]["NS.objects"][1]["NS.objects"] CIP_attributes = dict(zip(CIP_keys,CIP_values)) print "\tCustomItemProperties:" for key in CIP_attributes: print "\t " + key + ": " + str(CIP_attributes[key]) if "LSSharedFileList.RecentHosts" not in MRUFile: blob = attributes["Bookmark"] BLOBParser_raw(blob) BLOBParser_human(blob) BLOB_hex(blob) except: print "Cannot open file: " + MRUFile
def ParseSFL2(MRUFile): try: plistfile = open(MRUFile, "rb") plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) try: if plist_objects["root"]["NS.objects"][1]["NS.keys"][ 0] == "com.apple.LSSharedFileList.MaxAmount": numberOfItems = plist_objects["root"]["NS.objects"][1][ "NS.objects"][0] print("Max number of recent items in this plist: " + str(numberOfItems)) except: pass if plist_objects["root"]["NS.keys"][0] == "items": items = plist_objects["root"]["NS.objects"][0]["NS.objects"] for n, item in enumerate(items): attribute_keys = plist_objects["root"]["NS.objects"][0][ "NS.objects"][n]["NS.keys"] attribute_values = plist_objects["root"]["NS.objects"][0][ "NS.objects"][n]["NS.objects"] attributes = dict(list(zip(attribute_keys, attribute_values))) try: uuid = attributes["uuid"] except: uuid = "No 'UUID' Attribute" try: visability = str(attributes["visibility"]) except: visability = "No 'Visability' Attribute" try: name = attributes["Name"] except: name = "No 'Name' Attribute (Use BLOB parser for name)" print(" [Item Number: " + str(n) + " | (UUID:'" + uuid + "') | Visibility: " + visability + "] Name:'" + name + "'") # Unknown "CustomItemProperties" - Only seen blank, uncomment to see details. # print attributes["CustomItemProperties"] if "LSSharedFileList.RecentHosts" not in MRUFile: blob = attributes["Bookmark"] BLOBParser_raw(blob) BLOBParser_human(blob) BLOB_hex(blob) except: print("Cannot open file: " + MRUFile)
def ParseSFL2(MRUFile): try: plistfile = open(MRUFile, "rb") plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) try: if plist_objects["root"]["NS.objects"][1]["NS.keys"][0] == "com.apple.LSSharedFileList.MaxAmount": numberOfItems = plist_objects["root"]["NS.objects"][1]["NS.objects"][0] print "Max number of recent items in this plist: " + str(numberOfItems) except: pass if plist_objects["root"]["NS.keys"][0] == "items": items = plist_objects["root"]["NS.objects"][0]["NS.objects"] for n,item in enumerate(items): attribute_keys = plist_objects["root"]["NS.objects"][0]["NS.objects"][n]["NS.keys"] attribute_values = plist_objects["root"]["NS.objects"][0]["NS.objects"][n]["NS.objects"] attributes = dict(zip(attribute_keys,attribute_values)) try: uuid = attributes["uuid"] except: uuid = "No 'UUID' Attribute" try: visability = str(attributes["visibility"]) except: visability = "No 'Visability' Attribute" try: name = attributes["Name"] except: name = "No 'Name' Attribute (Use BLOB parser for name)" print " [Item Number: " + str(n) + " | (UUID:'" + uuid + "') | Visibility: " + visability + "] Name:'" + name + "'" #Unknown "CustomItemProperties" - Only seen blank, uncomment to see details. #print attributes["CustomItemProperties"] if "LSSharedFileList.RecentHosts" not in MRUFile: blob = attributes["Bookmark"] BLOBParser_raw(blob) BLOBParser_human(blob) BLOB_hex(blob) except: print "Cannot open file: " + MRUFile
def main(): parser = argparse.ArgumentParser( description= "NeXTSTEP Interface Builder (NIB) file parser prints a list of all connections defined in a NIB file" ) parser.add_argument('nibfile', help='path to a NIB file') args = parser.parse_args() with open(args.nibfile, "rb") as rp: plist = ccl_bplist.load(rp) nib = ccl_bplist.deserialise_NsKeyedArchiver(plist) ibcons = nib['IB.objectdata']['NSConnections']['NS.objects'] for i in range(len(ibcons)): if 'NSLabel' not in ibcons[i]: continue lbl = ibcons[i]['NSLabel'] id = dict(nib['IB.objectdata']['NSConnections'])['NS.objects'][i].value sname = "NA" srcid = "NA" dname = "NA" dstid = "NA" if 'NSSource' in ibcons[i]: srcid = dict(ibcons[i])['NSSource'].value src = ibcons[i]['NSSource'] sname = getClassName(src) scontents = getContents(src) if 'NSDestination' in ibcons[i]: dstid = dict(ibcons[i])['NSDestination'].value dst = ibcons[i]['NSDestination'] dname = getClassName(dst) dcontents = getContents(dst) out = "%d: %s (%s) - %s " % (i, lbl, id, sname) if scontents: out += "[%s] " % scontents out += "(%s) ---> %s " % (srcid, dname) if dcontents: out += "[%s] " % dcontents out += "(%s)" % dstid print(out.encode("utf-8"))
def process_nsa_plist(input_path, f): '''Returns a deserialized plist. Input is NSKeyedArchive file. input_path field is not used''' global use_as_library try: if not use_as_library: print('Reading file .. ' + input_path) f = extract_nsa_plist(f) ccl_bplist.set_object_converter( ccl_bplist.NSKeyedArchiver_common_objects_convertor) plist = ccl_bplist.load(f) ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) root_names = getRootElementNames(f) top_level = [] for root_name in root_names: root = ns_keyed_archiver_obj[root_name] if not use_as_library: print('Trying to deserialize binary plist $top = {}'.format( root_name)) if isinstance(root, dict): plist = {} recurseCreatePlist(plist, root, ns_keyed_archiver_obj.object_table) if root_name.lower() != 'root': plist = {root_name: plist} elif isinstance(root, list): plist = [] recurseCreatePlist(plist, root, ns_keyed_archiver_obj.object_table) if root_name.lower() != 'root': plist = {root_name: plist} else: plist = {root_name: root} if len(root_names) == 1: top_level = plist else: # > 1 top_level.append(plist) except Exception as ex: print('Had an exception (error)') traceback.print_exc() return top_level
def DeserializeNSKeyedArchive(file_pointer): '''Pass an open file pointer (file must be opened as rb) and get a dict or list representation of the plist returned back ''' ccl_bplist.set_object_converter( ccl_bplist.NSKeyedArchiver_common_objects_convertor) plist = ccl_bplist.load(file_pointer) ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) root = ns_keyed_archiver_obj['root'] if isinstance(root, dict): plist = {} else: plist = [] recurseCreatePlist(plist, root) return plist
def GetProfileInfo(f): profiles = [] ccl_bplist.set_object_converter(ccl_bplist.NSKeyedArchiver_common_objects_convertor) plist = ccl_bplist.load(f) ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) md = ns_keyed_archiver_obj['mapData'] for item in md: if md[item]['NSEntityName'] == 'MCX_Profile': profile_attribs = md[item]['NSAttributeValues'] attributes = [] attributes.append(profile_attribs[0]) # UUID if domain user, else name attributes.append(profile_attribs[1]) # user name attributes.append(profile_attribs[2]) # date first logged in (if domain user), else name # Not interpreting other attributes at this time! profiles.append(attributes) return profiles
def ParseSFL(MRUFile): itemsLinkList = [] try: with open(MRUFile, "rb") as plistfile: plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) if plist_objects["root"]["NS.keys"][2] == "items": items = plist_objects["root"]["NS.objects"][2]["NS.objects"] for n, item in enumerate(items): # item["URL"]['NS.relative'] file:///xxx/xxx/xxx filePath = item["URL"]['NS.relative'][7:] # /xxx/xxx/xxx/ the last "/" make basename func not work if filePath[-1] == '/': filePath = filePath[:-1] itemsLinkList.append(filePath) return itemsLinkList except Exception as e: print(e)
def ParseSFL(MRUFile): try: plistfile = open(MRUFile, "rb") plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) try: if plist_objects["root"]["NS.objects"][1]["NS.keys"][ 0] == "com.apple.LSSharedFileList.MaxAmount": numberOfItems = plist_objects["root"]["NS.objects"][1][ "NS.objects"][0] print "Max number of recent items in this plist: " + str( numberOfItems) except: pass if plist_objects["root"]["NS.keys"][2] == "items": items = plist_objects["root"]["NS.objects"][2]["NS.objects"] for n, item in enumerate(items): try: name = item["name"] except: name = "No 'name' Key" print " [Item Number: " + str(n) + " | Order: " + str( item["order"]) + "] Name:'" + name + "' (URL:'" + item[ "URL"]['NS.relative'] + "')" #UNCOMMENT FOR UNIQUE IDENTIFIER HEXDUMP #print "----------------------------------------------------------------------------" #print "Hexdump of Unique Identifier: " #print hexdump.hexdump(item["uniqueIdentifier"]["NS.uuidbytes"]) #print "----------------------------------------------------------------------------" if "LSSharedFileList.RecentHosts" not in MRUFile: blob = item["bookmark"] BLOBParser_raw(blob) BLOBParser_human(blob) BLOB_hex(blob) except: print "Cannot open file: " + MRUFile
def ParseSFL2(MRUFile): itemsLinkList = [] try: with open(MRUFile, "rb") as plistfile: plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) if plist_objects["root"]["NS.keys"][0] == "items": for item in plist_objects["root"]["NS.objects"][0]["NS.objects"]: attributes = dict(zip(item["NS.keys"], item["NS.objects"])) if "Bookmark" in attributes: if isinstance(attributes["Bookmark"], str): itemLink = BLOBParser_human(attributes["Bookmark"]) else: itemLink = BLOBParser_human( attributes["Bookmark"]['NS.data']) itemsLinkList.append(itemLink) return itemsLinkList except Exception as e: print e
def decrypt_secretData(item): if item['keyclass'] >=6 : unwrapped_key = unwrap_key(item['encryptedSecretData_wrappedKey'], item['keyclass']) bplist = BytesIO(item['encryptedSecretData_ciphertext']) plist = ccl_bplist.load(bplist) secretDataDeserialized = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) authCode = secretDataDeserialized['root']['SFAuthenticationCode'] iv = secretDataDeserialized['root']['SFInitializationVector'] ciphertext = secretDataDeserialized['root']['SFCiphertext'] gcm = AES.new(unhexlify(unwrapped_key)[:32], AES.MODE_GCM, iv) decrypted = gcm.decrypt_and_verify(ciphertext, authCode) der_data = decode(decrypted)[0] for k in der_data: if 'Octet' in str(type(k[1])): item['decrypted'].update({str(k[0]) : bytes(k[1])}) else: item['decrypted'].update({str(k[0]) : str(k[1])}) return item
def main(): global usage if sys.version_info.major == 2: print( 'ERROR-This will not work with python2. Please run again with python3!' ) return argc = len(sys.argv) if argc < 2 or sys.argv[1].lower() == '-h': print(usage) return input_path = sys.argv[1] if not os.path.exists(input_path): print('Error, file does not exist! Check file path!\r\n') print(usage) return # All OK, process the file now try: f = open(input_path, 'rb') print('Reading file .. ' + input_path) ccl_bplist.set_object_converter( ccl_bplist.NSKeyedArchiver_common_objects_convertor) plist = ccl_bplist.load(f) ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) root = ns_keyed_archiver_obj['root'] print('Trying to deserialize binary plist ..') plist = {} recurseCreatePlist(plist, root) print('Writing it back out as .. ' + input_path + '_deserialized.plist') biplist.writePlist(plist, input_path + '_deserialized.plist') print('Done !') except Exception as ex: print('Had an exception (error)') traceback.print_exc()
def _unpack_top_level(f, plist_biplist_obj): '''Does the work to actually unpack the NSKeyedArchive's top level. Returns the top level object. ''' ccl_bplist.set_object_converter( ccl_bplist.NSKeyedArchiver_common_objects_convertor) plist = ccl_bplist.load(f) ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) root_names = _get_root_element_names(plist_biplist_obj) top_level = [] for root_name in root_names: root = ns_keyed_archiver_obj[root_name] if isinstance(root, dict): plist = {} _recurse_create_plist(plist, root, ns_keyed_archiver_obj.object_table) if root_name.lower() != 'root': plist = {root_name: plist} elif isinstance(root, list): plist = [] _recurse_create_plist(plist, root, ns_keyed_archiver_obj.object_table) if root_name.lower() != 'root': plist = {root_name: plist} else: plist = {root_name: root} if len(root_names) == 1: top_level = plist else: # > 1 top_level.append(plist) return top_level
def ParseSFL(MRUFile): try: plistfile = open(MRUFile, "rb") plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) try: if plist_objects["root"]["NS.objects"][1]["NS.keys"][0] == "com.apple.LSSharedFileList.MaxAmount": numberOfItems = plist_objects["root"]["NS.objects"][1]["NS.objects"][0] print "Max number of recent items in this plist: " + str(numberOfItems) except: pass if plist_objects["root"]["NS.keys"][2] == "items": items = plist_objects["root"]["NS.objects"][2]["NS.objects"] for n,item in enumerate(items): try: name = item["name"] except: name = "No 'name' Key" print" [Item Number: " + str(n) + " | Order: " + str(item["order"]) + "] Name:'" + name + "' (URL:'" + item["URL"]['NS.relative'] + "')" #UNCOMMENT FOR UNIQUE IDENTIFIER HEXDUMP #print "----------------------------------------------------------------------------" #print "Hexdump of Unique Identifier: " #print hexdump.hexdump(item["uniqueIdentifier"]["NS.uuidbytes"]) #print "----------------------------------------------------------------------------" if "LSSharedFileList.RecentHosts" not in MRUFile: blob = item["bookmark"] BLOBParser_raw(blob) BLOBParser_human(blob) BLOB_hex(blob) except: print "Cannot open file: " + MRUFile
def knowledgec(filefound): print(f'Incepted bplist extractions in knowlwdgeC.db executing.') iOSversion = versionf supportediOS = ['11', '12', '13'] if iOSversion not in supportediOS: print ("Unsupported version"+iOSversion) return() extension = '.bplist' dump = True #create directories outpath = reportfolderbase+'KnowledgeC Protobuf/' try: os.mkdir(outpath) os.mkdir(outpath+"clean/") os.mkdir(outpath+"/dirty") except OSError: print("Error making directories") #connect sqlite databases db = sqlite3.connect(filefound[0]) cursor = db.cursor() #variable initializations dirtcount = 0 cleancount = 0 intentc = {} intentv = {} cursor.execute(''' SELECT Z_PK, Z_DKINTENTMETADATAKEY__SERIALIZEDINTERACTION, Z_DKINTENTMETADATAKEY__INTENTCLASS, Z_DKINTENTMETADATAKEY__INTENTVERB FROM ZSTRUCTUREDMETADATA WHERE Z_DKINTENTMETADATAKEY__SERIALIZEDINTERACTION is not null ''') all_rows = cursor.fetchall() for row in all_rows: pkv = str(row[0]) pkvplist = pkv+extension f = row[1] intentclass = str(row[2]) intententverb = str(row[3]) output_file = open(outpath+'/dirty/D_Z_PK'+pkvplist, 'wb') #export dirty from DB output_file.write(f) output_file.close() g = open(outpath+'/dirty/D_Z_PK'+pkvplist, 'rb') plistg = ccl_bplist.load(g) if (iOSversion == '11'): ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver(plistg) newbytearray = ns_keyed_archiver_obj if (iOSversion == '12'): ns_keyed_archiver_objg = ccl_bplist.deserialise_NsKeyedArchiver(plistg) newbytearray = (ns_keyed_archiver_objg["NS.data"]) if (iOSversion == '13'): ns_keyed_archiver_objg = ccl_bplist.deserialise_NsKeyedArchiver(plistg) newbytearray = (ns_keyed_archiver_objg["NS.data"]) dirtcount = dirtcount+1 binfile = open(outpath+'/clean/C_Z_PK'+pkvplist, 'wb') binfile.write(newbytearray) binfile.close() #add to dictionaries intentc['C_Z_PK'+pkvplist] = intentclass intentv['C_Z_PK'+pkvplist] = intententverb cleancount = cleancount+1 h = open(outpath+'/Report.html', 'w') h.write('<html><body>') h.write('<h2>iOS ' + iOSversion + ' - KnowledgeC ZSTRUCTUREDMETADATA bplist report</h2>') h.write ('<style> table, th, td {border: 1px solid black; border-collapse: collapse;}</style>') h.write('<br/>') for filename in glob.glob(outpath+'/clean/*'+extension): p = open(filename, 'rb') cfilename = os.path.basename(filename) plist = ccl_bplist.load(p) ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True)#deserialize clean #Get dictionary values A = intentc.get(cfilename) B = intentv.get(cfilename) if A is None: A = 'No value' if B is None: A = 'No value' #print some values from clean bplist if iOSversion == '13': NSdata = (ns_keyed_archiver_obj['root']['intent']['backingStore']['bytes']) else: NSdata = (ns_keyed_archiver_obj["root"]["intent"]["backingStore"]["data"]["NS.data"]) parsedNSData = "" #Default true if dump == True: nsdata_file = outpath+'/clean/'+cfilename+'_nsdata.bin' binfile = open(nsdata_file, 'wb') if iOSversion == '13': binfile.write(ns_keyed_archiver_obj['root']['intent']['backingStore']['bytes']) else: binfile.write(ns_keyed_archiver_obj["root"]["intent"]["backingStore"]["data"]["NS.data"]) binfile.close() messages = ParseProto(nsdata_file) messages_json_dump = json.dumps(messages, indent=4, sort_keys=True, ensure_ascii=False) parsedNSData = str(messages_json_dump).encode(encoding='UTF-8',errors='ignore') NSstartDate = ccl_bplist.convert_NSDate((ns_keyed_archiver_obj["root"]["dateInterval"]["NS.startDate"])) NSendDate = ccl_bplist.convert_NSDate((ns_keyed_archiver_obj["root"]["dateInterval"]["NS.endDate"])) NSduration = ns_keyed_archiver_obj["root"]["dateInterval"]["NS.duration"] Siri = ns_keyed_archiver_obj["root"]["_donatedBySiri"] h.write(cfilename) h.write('<br />') h.write('Intent Class: '+str(A)) h.write('<br />') h.write('Intent Verb: '+str(B)) h.write('<br />') h.write('<table>') h.write('<tr>') h.write('<th>Data type</th>') h.write('<th>Value</th>') h.write('</tr>') #Donated by Siri h.write('<tr>') h.write('<td>Siri</td>') h.write('<td>'+str(Siri)+'</td>') h.write('</tr>') #NSstartDate h.write('<tr>') h.write('<td>NSstartDate</td>') h.write('<td>'+str(NSstartDate)+' Z</td>') h.write('</tr>') #NSsendDate h.write('<tr>') h.write('<td>NSendDate</td>') h.write('<td>'+str(NSendDate)+' Z</td>') h.write('</tr>') #NSduration h.write('<tr>') h.write('<td>NSduration</td>') h.write('<td>'+str(NSduration)+'</td>') h.write('</tr>') #NSdata h.write('<tr>') h.write('<td>NSdata</td>') h.write('<td>'+str(NSdata)+'</td>') h.write('</tr>') #NSdata better formatting if parsedNSData: h.write('<tr>') h.write('<td>NSdata - Protobuf Decoded</td>') h.write('<td><pre id=\"json\">'+str(parsedNSData).replace('\\n', '<br>')+'</pre></td>') h.write('</tr>') else: #This will only run if -nd is used h.write('<tr>') h.write('<td>NSdata - Protobuf</td>') h.write('<td>'+str(NSdata).replace('\\n', '<br>')+'</td>') h.write('</tr>') h.write('<table>') h.write('<br />') #print(NSstartDate) #print(NSendDate) #print(NSduration) #print(NSdata) #print('') print("") print("iOS - KnowledgeC ZSTRUCTUREDMETADATA bplist extractor") print("By: @phillmoore & @AlexisBrignoni") print("thinkdfir.com & abrignoni.com") print("") print("Bplists from the Z_DKINTENTMETADATAKEY__SERIALIZEDINTERACTION field.") print("Exported bplists (dirty): "+str(dirtcount)) print("Exported bplists (clean): "+str(cleancount)) print("") print(f'Triage report completed. See Reports.html.') print('Incepted bplist extractions in knowlwdgeC.db completed')
def get_protonMail(files_found, report_folder, seeker): data_list = [] p = Path(__file__).parents[1] my_path = Path(p).joinpath('keychain') #platform = is_platform_windows() #if platform: # my_path = my_path.replace('/', '\\') if len(os.listdir(my_path)) == 1: logfunc("No keychain provided") return else: file = os.listdir(my_path) for x in file: if x.endswith('plist'): keychain_plist_path = Path(my_path).joinpath(x) for file_found in files_found: if 'group.ch.protonmail.protonmail.plist' in file_found: plist_name = file_found if file_found.endswith('ProtonMail.sqlite'): db_name = file_found with open(keychain_plist_path, 'rb') as f: plist = plistlib.load(f) keychainVal = {} if type(plist) == list: for dd in plist: if type(dd) == dict: if 'svce' in dd: if 'protonmail' in str(dd['svce']): #print(dd) keychainVal[dd['acct']] = dd['v_Data'] else: for d in plist: for dd in plist[d]: if type(dd) == dict: if 'svce' in dd: if 'protonmail' in str(dd['svce']): #print(dd) keychainVal[dd['acct']] = dd['v_Data'] mainKey = keychainVal[b'NoneProtection'] IVsize = 16 def decryptWithMainKey(encrypted): iv = encrypted[:IVsize] cipher = AES.new(mainKey, AES.MODE_CTR, initial_value=iv, nonce=b'') return cipher.decrypt(encrypted[IVsize:]) with open(plist_name, 'rb') as p: prefplist = plistlib.load(p) enc_val = prefplist.get('authKeychainStoreKeyProtectedWithMainKey', 'empty') if enc_val != 'empty': pass elif keychainVal[b'authKeychainStoreKeyProtectedWithMainKey']: enc_val = keychainVal[b'authKeychainStoreKeyProtectedWithMainKey'] else: logfunc( 'Decryption key not found in the keychain or the application plist.' ) return dec_val = decryptWithMainKey(enc_val) keychainStorePlist1 = ccl_bplist.load(BytesIO(dec_val)) keychainStorePlist = ccl_bplist.load(BytesIO(keychainStorePlist1[0])) keychainStore = ccl_bplist.deserialise_NsKeyedArchiver( keychainStorePlist, parse_whole_structure=True) privateKeyCoderKey = keychainStore['root']['NS.objects'][0][ 'privateKeyCoderKey'] key, _ = pgpy.PGPKey.from_blob(privateKeyCoderKey) pwdKey = keychainStore['root']['NS.objects'][0]['AuthCredential.Password'] def decrypt_message(encm): if ('-----BEGIN PGP MESSAGE-----') in encm: with key.unlock(pwdKey): assert key.is_unlocked message_from_blob = pgpy.PGPMessage.from_blob(encm) decm = key.decrypt(message_from_blob).message #print(decm) return html.unescape( decm.encode('cp1252', errors='ignore').decode('utf8', errors='ignore')) else: return encm def decrypt_attachment(proton_path, out_path, key, pwdKey, keyPacket, encfilename, decfilename): att = None for r, _, f in os.walk(proton_path): for file in f: if encfilename in file: att = os.path.join(r, file) break if att: with key.unlock(pwdKey): assert key.is_unlocked with open(att, 'rb') as attfh: buf = b64decode(keyPacket) buf += attfh.read() att_from_blob = pgpy.PGPMessage.from_blob(buf) decatt = key.decrypt(att_from_blob).message itemnum = str(random.random()) with open(os.path.join(out_path, itemnum + decfilename), 'wb') as outatt: outatt.write(decatt) return os.path.join(out_path, itemnum + decfilename) return None db = open_sqlite_db_readonly(db_name) cursor = db.cursor() cursor.execute('''SELECT ZMESSAGE.ZTIME, ZMESSAGE.ZBODY, ZMESSAGE.ZMIMETYPE, ZMESSAGE.ZTOLIST, ZMESSAGE.ZREPLYTOS, ZMESSAGE.ZSENDER, ZMESSAGE.ZTITLE, ZMESSAGE.ZISENCRYPTED, ZMESSAGE.ZNUMATTACHMENTS, ZATTACHMENT.ZFILESIZE, ZATTACHMENT.ZFILENAME, ZATTACHMENT.ZMIMETYPE, ZATTACHMENT.ZHEADERINFO, ZATTACHMENT.ZLOCALURL, ZATTACHMENT.ZKEYPACKET FROM ZMESSAGE LEFT JOIN ZATTACHMENT ON ZMESSAGE.Z_PK = ZATTACHMENT.ZMESSAGE ''') all_rows = cursor.fetchall() data_list = [] if len(all_rows) > 0: for row in all_rows: aggregatorto = '' aggregatorfor = '' time = row[0] decryptedtime = datetime.fromtimestamp(time + 978307200) decryptedbody = decrypt_message(row[1]) mime = row[2] to = json.loads(plistlib.loads(decryptWithMainKey(row[3]))[0]) for r in to: address = r['Address'] name = r['Name'] aggregatorto = f"{address} {name}" try: replyto = json.loads( plistlib.loads(decryptWithMainKey(row[4]))[0]) for r in replyto: address = r['Address'] name = r['Name'] aggregatorfor = f"{address} {name}" except: aggregatorfor = '' try: sender = json.loads( plistlib.loads(decryptWithMainKey(row[5]))[0]) name = sender['Name'] address = sender['Address'] sender_info = f'{address} {name}' except: sender_info = '<Not Decoded>' title = plistlib.loads(decryptWithMainKey(row[6]))[0] isencrypted = row[7] ZNUMATTACHMENTS = row[8] ZFILESIZE = row[9] try: ZFILENAME = plistlib.loads(decryptWithMainKey(row[10]))[0] except: ZFILENAME = "" try: AMIMETYPE = plistlib.loads(decryptWithMainKey(row[11]))[0] except: AMIMETYPE = "" if row[12]: zheaderinfo = decryptWithMainKey(row[12]) attpath = '' if row[13]: encfilename = plistlib.loads( row[13])['$objects'][2].split('/')[-1] guidi = plistlib.loads(row[13])['$objects'][2].split('/')[-4] out_path = report_folder for match in files_found: if f'/Data/Application/{guidi}/tmp/attachments' in match: proton_path = match.split('/attachments')[0] break elif f'\\Data\\Application\\{guidi}\\tmp\\attachments' in match: proton_path = match.split('\\attachments')[0] attpath = decrypt_attachment(proton_path, out_path, key, pwdKey, row[14], encfilename, ZFILENAME) mimetype = magic.from_file(attpath, mime=True) if 'video' in mimetype: attpath = f'<video width="320" height="240" controls="controls"><source src="{attpath}" type="video/mp4">Your browser does not support the video tag.</video>' elif 'image' in mimetype: attpath = f'<img src="{attpath}"width="300"></img>' else: attpath = f'<a href="{attpath}"> Link to {mimetype} </>' data_list.append( (decryptedtime, sender_info, aggregatorto, aggregatorfor, title, decryptedbody, mime, isencrypted, ZFILESIZE, attpath, ZFILENAME, AMIMETYPE)) encfilename = '' if len(data_list) > 0: report = ArtifactHtmlReport('Proton Mail - Decrypted Emails') report.start_artifact_report(report_folder, 'Proton Mail - Decrypted Emails') report.add_script() data_headers = ('Timestamp', 'Sender', 'To', 'Reply To', 'Title', 'Body', 'Mime', 'Is encrypted?', 'File Size', 'Attachment', 'Decrypted Attachment Filename', 'Type') report.write_artifact_data_table(data_headers, data_list, file_found, html_no_escape=[ 'Sender', 'To', 'Reply To', 'Body', 'File Name', 'Type', 'Attachment' ]) report.end_artifact_report() tsvname = 'Proton Mail - Decrypted Emails' tsv(report_folder, data_headers, data_list, tsvname) tlactivity = 'Proton Mail - Decrypted Emails' timeline(report_folder, tlactivity, data_list, data_headers) else: logfunc('No Proton Mail - Decrypted Emails')
def deserialize_plist(path_or_file): ''' Returns a deserialized plist as a dictionary/list. Parameters ---------- path_or_file: Path or file-like object of an NSKeyedArchive file Returns ------- A dictionary or list is returned depending on contents of the plist Exceptions ---------- nska_deserialize.DeserializeError, biplist.NotBinaryPlistException, ccl_bplist.BplistError, ValueError, TypeError, OSError, OverflowError ''' path = '' f = None if isinstance(path_or_file, str): path = path_or_file f = open(path, 'rb') else: # its a file f = path_or_file f = _get_valid_nska_plist(f) ccl_bplist.set_object_converter( ccl_bplist.NSKeyedArchiver_common_objects_convertor) plist = ccl_bplist.load(f) ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) root_names = _get_root_element_names(f) top_level = [] for root_name in root_names: root = ns_keyed_archiver_obj[root_name] if isinstance(root, dict): plist = {} _recurse_create_plist(plist, root, ns_keyed_archiver_obj.object_table) if root_name.lower() != 'root': plist = {root_name: plist} elif isinstance(root, list): plist = [] _recurse_create_plist(plist, root, ns_keyed_archiver_obj.object_table) if root_name.lower() != 'root': plist = {root_name: plist} else: plist = {root_name: root} if len(root_names) == 1: top_level = plist else: # > 1 top_level.append(plist) return top_level
def ParseSFL2_FavoriteVolumes(MRUFile): try: plistfile = open(MRUFile, "rb") plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) print("Item number has no bearing upon time of usage.") print("Plist Properties:") if plist_objects["root"]["NS.keys"][1] == "properties": properties_keys = plist_objects["root"]["NS.objects"][1]["NS.keys"] properties_values = plist_objects["root"]["NS.objects"][1][ "NS.objects"] properties = dict(list(zip(properties_keys, properties_values))) for key in properties: print(" " + key + ": " + str(properties[key])) if plist_objects["root"]["NS.keys"][0] == "items": items = plist_objects["root"]["NS.objects"][0]["NS.objects"] for n, item in enumerate(items): attribute_keys = plist_objects["root"]["NS.objects"][0][ "NS.objects"][n]["NS.keys"] attribute_values = plist_objects["root"]["NS.objects"][0][ "NS.objects"][n]["NS.objects"] attributes = dict(list(zip(attribute_keys, attribute_values))) try: uuid = attributes["uuid"] except: uuid = "No 'UUID' Attribute" try: visability = str(attributes["visibility"]) except: visability = "No 'Visability' Attribute" try: name = attributes["Name"] except: name = "No 'Name' Attribute (Use BLOB parser for name)" print("\n [Item Number: " + str(n) + " | (UUID:'" + uuid + "') | Visibility: " + visability + "] Name: '" + name + "'") if attributes["CustomItemProperties"]: CIP_keys = plist_objects["root"]["NS.objects"][0][ "NS.objects"][n]["NS.objects"][1]["NS.keys"] CIP_values = plist_objects["root"]["NS.objects"][0][ "NS.objects"][n]["NS.objects"][1]["NS.objects"] CIP_attributes = dict(list(zip(CIP_keys, CIP_values))) print("\tCustomItemProperties:") for key in CIP_attributes: print("\t " + key + ": " + str(CIP_attributes[key])) if "LSSharedFileList.RecentHosts" not in MRUFile: blob = attributes["Bookmark"] BLOBParser_raw(blob) BLOBParser_human(blob) BLOB_hex(blob) except: print("Cannot open file: " + MRUFile)
\n\t\thexdump.py: https://pypi.python.org/pypi/hexdump\ \n\t\tccl_bplist.py: https://github.com/jorik041/ccl-bplist' , prog='dump_freq_locs.py' , formatter_class=RawTextHelpFormatter) parser.add_argument('-output', choices=['k','c','e'], action="store", help="k=KML, c=CSV, e=EVERTHING") parser.add_argument('State_Model_Plist_File') args = parser.parse_args() global output_type output_type = None statemodelfile = args.State_Model_Plist_File plistfile = open(statemodelfile, "rb") plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) objects = plist_objects["root"]["stateModelLut"]['NS.objects'] metadata_objects = plist["$top"] meta_uids = plist_objects["root"] if args.output == 'c' or args.output == 'e': output_type = 'c' with open('dump_freq_locs_output.csv', 'wb') as csvfile: loccsv = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) loccsv.writerow(['Timestamp', 'Timestamp Type','Data Type', 'Latitude', 'Longitude', 'Other Data']) getMetadata() RTVisitMonitor() getLocations() if args.output == 'k' or args.output =='e':
def decode(self, plist): try: return ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=True) except: return None
for row in all_rows: pkv = str(row[0]) pkvplist = pkv+extension f = row[1] intentclass = str(row[2]) intententverb = str(row[3]) output_file = open('./'+foldername+'/dirty/D_Z_PK'+pkvplist, 'wb') #export dirty from DB output_file.write(f) output_file.close() g = open('./'+foldername+'/dirty/D_Z_PK'+pkvplist, 'rb') plistg = ccl_bplist.load(g) if (iOSversion == '11'): ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver(plistg) newbytearray = ns_keyed_archiver_obj if (iOSversion == '12'): ns_keyed_archiver_objg = ccl_bplist.deserialise_NsKeyedArchiver(plistg) newbytearray = (ns_keyed_archiver_objg["NS.data"]) dirtcount = dirtcount+1 binfile = open('./'+foldername+'/clean/C_Z_PK'+pkvplist, 'wb') binfile.write(newbytearray) binfile.close() #add to dictionaries intentc['C_Z_PK'+pkvplist] = intentclass intentv['C_Z_PK'+pkvplist] = intententverb
formatter_class=RawTextHelpFormatter) parser.add_argument('-output', choices=['k', 'c', 'e'], action="store", help="k=KML, c=CSV, e=EVERTHING") parser.add_argument('State_Model_Plist_File') args = parser.parse_args() global output_type output_type = None statemodelfile = args.State_Model_Plist_File plistfile = open(statemodelfile, "rb") plist = ccl_bplist.load(plistfile) plist_objects = ccl_bplist.deserialise_NsKeyedArchiver( plist, parse_whole_structure=True) objects = plist_objects["root"]["stateModelLut"]['NS.objects'] metadata_objects = plist["$top"] meta_uids = plist_objects["root"] if args.output == 'c' or args.output == 'e': output_type = 'c' with open('dump_freq_locs_output.csv', 'wb') as csvfile: loccsv = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) loccsv.writerow([ 'Timestamp', 'Timestamp Type', 'Data Type', 'Latitude', 'Longitude', 'Other Data' ])
def applicationstate(filefound): iOSversion = versionf print(f'ApplicationState.db queries executing.') outpath = reportfolderbase +'Application State/' try: os.mkdir(outpath) os.mkdir(outpath+"exported-dirty/") os.mkdir(outpath+"exported-clean/") except OSError: print("Error making directories") freepath = 1 for pathfile in filefound: if isinstance(pathfile, pathlib.PurePath): freepath = os.path.abspath(pathfile) if freepath.endswith('.db'): apstatefiledb = freepath elif pathfile.endswith('.db'): apstatefiledb = pathfile #connect sqlite databases db = sqlite3.connect(apstatefiledb) cursor = db.cursor() cursor.execute(''' select application_identifier_tab.[application_identifier], kvs.[value] from kvs, key_tab,application_identifier_tab where key_tab.[key]='compatibilityInfo' and kvs.[key] = key_tab.[id] and application_identifier_tab.[id] = kvs.[application_identifier] order by application_identifier_tab.[id] ''') all_rows = cursor.fetchall() #poner un try except por si acaso extension = '.bplist' count = 0 for row in all_rows: bundleid = str(row[0]) bundleidplist = bundleid+'.bplist' f = row[1] output_file = open(outpath+'/exported-dirty/'+bundleidplist, 'wb') #export dirty from DB output_file.write(f) output_file.close() g = open(outpath+'/exported-dirty/'+bundleidplist, 'rb') #plist = plistlib.load(g) plist = ccl_bplist.load(g) output_file = open(outpath+'exported-clean/'+bundleidplist, 'wb') output_file.write(plist) output_file.close() #create html headers filedatahtml = open(outpath+'Application State.html', mode='a+') filedatahtml.write('<html><body>') filedatahtml.write('<h2>iOS ApplicationState.db Report </h2>') filedatahtml.write ('<style> table, th, td {border: 1px solid black; border-collapse: collapse;}</style>') filedatahtml.write('<br/>') filedatahtml.write('<table>') filedatahtml.write(f'<tr><td colspan = "4">{apstatefiledb}</td></tr>') filedatahtml.write('<tr><td>Bundle ID</td><td>Bundle Path</td><td>Bundle Container</td><td>Sandbox Path</td></tr>') for filename in glob.glob(outpath+'exported-clean/*.bplist'): p = open(filename, 'rb') #cfilename = os.path.basename(filename) plist = ccl_bplist.load(p) ns_keyed_archiver_obj = ccl_bplist.deserialise_NsKeyedArchiver(plist, parse_whole_structure=False)#deserialize clean #print(cfilename) bid = (ns_keyed_archiver_obj['bundleIdentifier']) bpath = (ns_keyed_archiver_obj['bundlePath']) bcontainer = (ns_keyed_archiver_obj['bundleContainerPath']) bsandbox = (ns_keyed_archiver_obj['sandboxPath']) if bsandbox == '$null': bsandbox = '' if bcontainer == '$null': bcontainer = '' #csv report filedata = open(outpath+'ApplicationState_InstalledAppInfo.csv', mode='a+') filewrite = csv.writer(filedata, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) filewrite.writerow([bid, bpath, bcontainer, bsandbox]) count = count + 1 filedata.close() #html report filedatahtml.write(f'<tr><td>{bid}</td><td>{bpath}</td><td>{bcontainer}</td><td>{bsandbox}</td></tr>') filemetadata = open(outpath+'ApplicationState_InstalledAppInfo_Path.txt', mode='w') filemetadata.write(f'Artifact name and file path: {apstatefiledb} ') filemetadata.close() #close html footer filedatahtml.write('</table></html>') filedatahtml.close() print(f'Installed app GUIDs and app locations processed: {count}') print(f'ApplicationState.db queries completed.')