def crack(manifest, passwords, notify=None): manifest = readPlist(manifest) if not manifest["IsEncrypted"]: print "Backup is not encrypted" return iosFlag = 'ManifestKey' in manifest kb = Keybag(manifest["BackupKeyBag"].data) kb.deviceKey = None if kb.type != BACKUP_KEYBAG and kb.type != OTA_KEYBAG: print "Backup does not contain a backup keybag" return salt = kb.attrs["SALT"] iter = kb.attrs["ITER"] print 'iter', iter dpsl = None dpic = None if iosFlag: dpsl = kb.attrs["DPSL"] dpic = kb.attrs["DPIC"] print 'dpic', dpic res = None for password in passwords: password = password.strip() if not password: continue print "[%s]: Trying Crack" % (password) stime = time() res = try_password(password, iosFlag, dpsl, dpic, salt, iter, kb) etime = time() print "[%s]: Take time %s" % (password, etime - stime) if res: print 'Find Password: ', password return password
def download(self, backupUDID): mbsbackup = self.getBackup(backupUDID) print "Downloading backup %s" % backupUDID.encode("hex") self.outputFolder = os.path.join(self.outputFolder, backupUDID.encode("hex")) makedirs(self.outputFolder) print backup_summary(mbsbackup) #print mbsbackup.Snapshot.Attributes.KeybagUUID.encode("hex") keys = self.getKeys(backupUDID) if not keys or not len(keys.Key): print "getKeys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return for snapshot in xrange(1, mbsbackup.Snapshot.SnapshotID + 1): files = self.listFiles(backupUDID, snapshot) print "%d files" % len(files) files2 = [] for f in files: if f.Attributes.EncryptionKey: files2.append(f) print f if len(files2): authTokens = self.getFiles(backupUDID, snapshot, files) self.authorizeGet(authTokens)
def getBackupKeyBag(backupfolder, passphrase): manifest = readPlist(backupfolder + "/Manifest.plist") kb = Keybag(manifest["BackupKeyBag"].data) if kb.unlockBackupKeybagWithPasscode(passphrase): print "BackupKeyBag unlock OK" return kb else: return None
def getBackupKeyBag(backupfolder, passphrase): manifest = BPlistReader.plistWithFile(backupfolder + "/Manifest.plist") kb = Keybag(manifest["BackupKeyBag"].data) if kb.unlockBackupKeybagWithPasscode(passphrase): print "BackupKeyBag unlock OK" return kb else: return None
def __init__(self, bdev, device_infos, **kwargs): super(EMFVolume,self).__init__(bdev, **kwargs) volumeid = self.volumeID().encode("hex") if not device_infos: dirname = os.path.dirname(bdev.filename) device_infos = search_plist(dirname, {"dataVolumeUUID":volumeid}) if not device_infos: raise Exception("Missing keyfile") try: self.emfkey = None if device_infos.has_key("EMF"): self.emfkey = device_infos["EMF"].decode("hex") self.lbaoffset = device_infos["dataVolumeOffset"] self.keybag = Keybag.createWithPlist(device_infos) except: raise #Exception("Invalid keyfile") self.decrypted = (self.header.finderInfo[3] == FLAG_DECRYPTED) rootxattr = self.getXattr(kHFSRootParentID, "com.apple.system.cprotect") self.cp_major_version = None self.cp_root = None if rootxattr == None: print "(No root com.apple.system.cprotect xattr)" else: self.cp_root = cp_root_xattr.parse(rootxattr) ver = self.cp_root.major_version print "cprotect version : %d" % ver assert self.cp_root.major_version == 2 or self.cp_root.major_version == 4 self.cp_major_version = self.cp_root.major_version self.keybag = loadKeybagFromVolume(self, device_infos)
def __init__(self, file, **kwargs): super(EMFVolume, self).__init__(file, **kwargs) pl = "%s.plist" % self.volumeID().encode("hex") dirname = os.path.dirname(file) if dirname != "": pl = dirname + "/" + pl if not os.path.exists(pl): raise Exception("Missing keyfile %s" % pl) try: pldict = plistlib.readPlist(pl) self.emfkey = pldict["EMF"].decode("hex") self.lbaoffset = pldict["dataVolumeOffset"] self.keystore = Keybag.createWithPlist(pldict) except: raise #Exception("Invalid keyfile") rootxattr = self.getXattr(kHFSRootParentID, "com.apple.system.cprotect") self.cp_major_version = None if rootxattr == None: print "Not an EMF image, no root com.apple.system.cprotec xattr" else: self.cp_root = cp_root_xattr.parse(rootxattr) print "cprotect version :", self.cp_root.major_version assert self.cp_root.major_version == 2 or self.cp_root.major_version == 4 self.cp_major_version = self.cp_root.major_version
def extract_backup(backup_path, output_path, password=""): if not os.path.exists(backup_path + "/Manifest.plist"): print "Manifest.plist not found" return manifest = readPlist(backup_path + "/Manifest.plist") info = readPlist( backup_path + "/Info.plist") for i in showinfo: print i + " : " + unicode(info.get(i, "missing")) if manifest["IsEncrypted"] and password == "": password = getpass('Enter backup password : ') if not manifest.has_key("BackupKeyBag"): print "oops this is not encrypted" exit(1) else: mbdb = MBDB(backup_path) kb = Keybag.createWithBackupManifest(manifest, password) if not kb: return manifest["password"] = password mbdb.keybag = kb extract_sms_db(mbdb, output_path)
def escrow(): client = RamdiskToolClient() di = client.getDeviceInfos() key835 = di.get("key835").decode("hex") plist = os.environ[ "ALLUSERSPROFILE"] + "/Apple/Lockdown/%s.plist" % di["udid"] lockdown = plistlib.readPlist(plist) kb = Keybag.createWithDataSignBlob(lockdown["EscrowBag"].data, key835) keybags = di.setdefault("keybags", {}) kbuuid = kb.uuid.encode("hex") if not keybags.has_key(kbuuid): print lockdown["HostID"] res = client.getEscrowRecord(lockdown["HostID"]) bagkey = res.get("BagKey") print "Bag key" + bagkey.data.encode("hex") res = client.getPasscodeKey(lockdown["EscrowBag"].data, bagkey) print res passcodeKey = res["passcodeKey"].decode("hex") keybags[kbuuid] = { "KeyBagKeys": lockdown["EscrowBag"], "passcode": bagkey, "passcodeKey": passcodeKey.encode("hex") } pl.update(keybags[kbuuid]) else: passcodeKey = keybags[kbuuid].get("passcodeKey").decode("hex") print kb.unlockWithPasscodeKey(passcodeKey) kb.printClassKeys()
def download(self, backupUDID): mbsbackup = self.getBackup(backupUDID) print "Downloading backup %s" % backupUDID.encode("hex") self.outputFolder = os.path.join(self.outputFolder, backupUDID.encode("hex")) makedirs(self.outputFolder) print backup_summary(mbsbackup) #print mbsbackup.Snapshot.Attributes.KeybagUUID.encode("hex") keys = self.getKeys(backupUDID) if not keys or not len(keys.Key): print "getKeys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return for snapshot in xrange(1, mbsbackup.Snapshot.SnapshotID+1): files = self.listFiles(backupUDID, snapshot) print "%d files" % len(files) files2 = [] for f in files: if f.Attributes.EncryptionKey: files2.append(f) print f if len(files2): authTokens = self.getFiles(backupUDID, snapshot, files) self.authorizeGet(authTokens)
def escrow(): client = RamdiskToolClient() di = client.getDeviceInfos() key835 = di.get("key835").decode("hex") plist = os.environ["ALLUSERSPROFILE"] + "/Apple/Lockdown/%s.plist" % di["udid"] lockdown = plistlib.readPlist(plist) kb = Keybag.createWithDataSignBlob(lockdown["EscrowBag"].data, key835) keybags = di.setdefault("keybags", {}) kbuuid = kb.uuid.encode("hex") if not keybags.has_key(kbuuid): print lockdown["HostID"] res = client.getEscrowRecord(lockdown["HostID"]) bagkey = res.get("BagKey") print "Bag key" + bagkey.data.encode("hex") res = client.getPasscodeKey(lockdown["EscrowBag"].data, bagkey) print res passcodeKey = res["passcodeKey"].decode("hex") keybags[kbuuid] = {"KeyBagKeys": lockdown["EscrowBag"], "passcode": bagkey, "passcodeKey": passcodeKey.encode("hex")} pl.update(keybags[kbuuid]) else: passcodeKey = keybags[kbuuid].get("passcodeKey").decode("hex") print kb.unlockWithPasscodeKey(passcodeKey) kb.printClassKeys()
def bf_system(): client = RamdiskToolClient() di = client.getDeviceInfos() devicedir = di["udid"] if os.getcwd().find(devicedir) == -1: try: os.mkdir(devicedir) except: pass os.chdir(devicedir) key835 = di.get("key835").decode("hex") systembag = client.getSystemKeyBag() kbkeys = systembag["KeyBagKeys"].data kb = Keybag.createWithDataSignBlob(kbkeys, key835) keybags = di.setdefault("keybags", {}) kbuuid = kb.uuid.encode("hex") print "Keybag UUID :", kbuuid if True and keybags.has_key(kbuuid) and keybags[kbuuid].has_key("passcodeKey"): print "We've already seen this keybag" passcodeKey = keybags[kbuuid].get("passcodeKey").decode("hex") print kb.unlockWithPasscodeKey(passcodeKey) kb.printClassKeys() else: keybags[kbuuid] = {"KeyBagKeys": systembag["KeyBagKeys"]} di["KeyBagKeys"] = systembag["KeyBagKeys"] di.save() print "Enter passcode or leave blank for bruteforce:" z = raw_input() res = client.getPasscodeKey(systembag["KeyBagKeys"].data, z) if kb.unlockWithPasscodeKey(res.get("passcodeKey").decode("hex")): print "Passcode \"%s\" OK" % z di.update(res) keybags[kbuuid].update(res) di.save() keychain_blob = client.downloadFile("/mnt2/Keychains/keychain-2.db") write_file("keychain-2.db", keychain_blob) print "Downloaded keychain database, use keychain_tool.py to decrypt secrets" return if z != "": print "Wrong passcode, trying to bruteforce !" if checkPasscodeComplexity(client) == 0: print "Trying all 4-digits passcodes..." bf = client.bruteforceKeyBag(systembag["KeyBagKeys"].data) if bf: di.update(bf) keybags[kbuuid].update(bf) print bf print kb.unlockWithPasscodeKey(bf.get("passcodeKey").decode("hex")) kb.printClassKeys() di["classKeys"] = kb.getClearClassKeysDict() di.save() else: print "Complex passcode used !" return #keychain_blob = client.downloadFile("/private/var/Keychains/keychain-2.db") keychain_blob = client.downloadFile("/mnt2/Keychains/keychain-2.db") write_file("keychain-2.db", keychain_blob) print "Downloaded keychain database, use keychain_tool.py to decrypt secrets"
def __init__(self, bdev, device_infos, **kwargs): super(EMFVolume, self).__init__(bdev, **kwargs) volumeid = self.volumeID().encode("hex") if not device_infos: dirname = os.path.dirname(bdev.filename) device_infos = search_plist(dirname, {"dataVolumeUUID": volumeid}) if not device_infos: raise Exception("Missing keyfile") try: self.emfkey = None if device_infos.has_key("EMF"): self.emfkey = device_infos["EMF"].decode("hex") self.lbaoffset = device_infos["dataVolumeOffset"] self.keybag = Keybag.createWithPlist(device_infos) except: raise #Exception("Invalid keyfile") rootxattr = self.getXattr(kHFSRootParentID, "com.apple.system.cprotect") self.decrypted = (self.header.finderInfo[3] == FLAG_DECRYPTED) self.cp_major_version = None self.cp_root = None if rootxattr == None: print "(No root com.apple.system.cprotect xattr)" else: self.cp_root = cp_root_xattr.parse(rootxattr) ver = self.cp_root.major_version print "cprotect version : %d (iOS %d)" % (ver, 4 + int(ver != 2)) assert self.cp_root.major_version == 2 or self.cp_root.major_version == 4 self.cp_major_version = self.cp_root.major_version self.keybag = loadKeybagFromVolume(self, device_infos)
def loadKeybagFromVolume(volume, device_infos): systembag = volume.readFile("/keybags/systembag.kb", returnString=True) if not systembag or not systembag.startswith("bplist"): print "FAIL: could not read /keybags/systembag.kb from data partition" return False lockers = EffaceableLockers(device_infos["lockers"].data) bag1key = lockers.get("BAG1")[-32:] keybag = Keybag.createWithSystemkbfile(systembag, bag1key, device_infos.get("key835", "").decode("hex")) keybag.setDKey(device_infos) if device_infos.has_key("passcodeKey"): keybag.unlockWithPasscodeKey(device_infos.get("passcodeKey").decode("hex")) return keybag
def download(self, backupUDID, fast): mbsbackup = self.getBackup(backupUDID) print "Downloading backup %s" % backupUDID.encode("hex") self.outputFolder = os.path.join(self.outputFolder, backupUDID.encode("hex")) makedirs(self.outputFolder) #print backup_summary(mbsbackup) #print mbsbackup.Snapshot.Attributes.KeybagUUID.encode("hex") keys = self.getKeys(backupUDID) if not keys or not len(keys.Key): print "getKeys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return print "Available Snapshots: ", mbsbackup.Snapshot.SnapshotID for snapshot in xrange(1, mbsbackup.Snapshot.SnapshotID + 1): print "Listing snapshot..." files = self.listFiles(backupUDID, snapshot) print "Files in snapshot %s : %s" % (snapshot, len(files)) files2 = [] if fast == 'y': for f in files: if 'AddressBook.sqlitedb' in f.RelativePath or 'Calendar.sqlitedb' in f.RelativePath or 'sms.db' in f.RelativePath or 'call_history.db' in f.RelativePath or '.JPG' in f.RelativePath: files2.append(f) files = files2 if len(files): authTokens = self.getFiles(backupUDID, snapshot, files) #print authTokens self.authorizeGet(authTokens, snapshot) if fast == 'n': if len(files): authTokens = self.getFiles(backupUDID, snapshot, files) #print authTokens self.authorizeGet(authTokens, snapshot)
def bf_system(): client = RamdiskToolClient() di = client.getDeviceInfos() devicedir = di["udid"] if os.getcwd().find(devicedir) == -1: try: os.mkdir(devicedir) except: pass os.chdir(devicedir) key835 = di.get("key835").decode("hex") systembag = client.getSystemKeyBag() kbkeys = systembag["KeyBagKeys"].data kb = Keybag.createWithDataSignBlob(kbkeys, key835) keybags = di.setdefault("keybags", {}) kbuuid = kb.uuid.encode("hex") print "Keybag UUID :", kbuuid if True and keybags.has_key(kbuuid) and keybags[kbuuid].has_key( "passcodeKey"): print "We've already seen this keybag" passcodeKey = keybags[kbuuid].get("passcodeKey").decode("hex") print kb.unlockWithPasscodeKey(passcodeKey) kb.printClassKeys() else: keybags[kbuuid] = {"KeyBagKeys": systembag["KeyBagKeys"]} di["KeyBagKeys"] = systembag["KeyBagKeys"] di.save() if checkPasscodeComplexity(client) == 0: print "Trying all 4-digits passcodes..." bf = client.bruteforceKeyBag(systembag["KeyBagKeys"].data) if bf: di.update(bf) keybags[kbuuid].update(bf) print bf print kb.unlockWithPasscodeKey(bf.get("passcodeKey").decode("hex")) kb.printClassKeys() di["classKeys"] = kb.getClearClassKeysDict() di.save() else: print "Complex passcode used !" return #keychain_blob = client.downloadFile("/private/var/Keychains/keychain-2.db") keychain_blob = client.downloadFile("/mnt2/Keychains/keychain-2.db") write_file("keychain-2.db", keychain_blob) print "Downloaded keychain database, use keychain_tool.py to decrypt secrets"
def extract_backup(backup_path, output_path, password=""): if not os.path.exists(backup_path + "/Manifest.plist"): print "Manifest.plist not found" return manifest = readPlist(backup_path + "/Manifest.plist") info = readPlist( backup_path + "/Info.plist") for i in showinfo: print i + " : " + unicode(info.get(i, "missing")) #jsc # print "Extract backup to %s ? (y/n)" % output_path # if raw_input() == "n": # return print "Backup is %sencrypted" % (int(not manifest["IsEncrypted"]) * "not ") #jsc # if manifest["IsEncrypted"] and password == "": # print "Enter backup password : "******"BackupKeyBag"): print "No BackupKeyBag in manifest, assuming iOS 3.x backup" decrypt_backup3(backup_path, output_path, password) else: mbdb = MBDB(backup_path) kb = Keybag.createWithBackupManifest(manifest, password) if not kb: return #jsc password = kb.bfPassword manifest["password"] = password makedirs(output_path) plistlib.writePlist(manifest, output_path + "/Manifest.plist") mbdb.keybag = kb mbdb.extract_backup(output_path) #jsc print "Bruteforce successful, backup password : %s" % password print "You can decrypt the keychain using the following command : " print "python keychain_tool.py -d \"%s\" \"%s\"" % (output_path + "/KeychainDomain/keychain-backup.plist", output_path + "/Manifest.plist")
def download(self, backupUDID, item_types): mbsbackup = self.get_backup(backupUDID) self.output_folder = os.path.join(self.output_folder, backupUDID.encode("hex")) print "Downloading backup {} to {}".format(backupUDID.encode("hex"), self.output_folder) try: mkdir_p(self.output_folder) except OSError: print "Directory \"{}\" already exists.".format(self.output_folder) return keys = self.get_keys(backupUDID) if not keys or not len(keys.Key): print "get_keys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return print "Available Snapshots: ", mbsbackup.Snapshot.SnapshotID #for snapshot in xrange(1, mbsbackup.Snapshot.SnapshotID+1): for snapshot in [1, mbsbackup.Snapshot.SnapshotID - 1, mbsbackup.Snapshot.SnapshotID]: print "Listing snapshot..." files = self.list_files(backupUDID, snapshot) print "Files in snapshot %s : %s" % (snapshot, len(files)) def matches_allowed_item_types(file): return any(ITEM_TYPES_TO_FILE_NAMES[item_type] in file.RelativePath \ for item_type in item_types) if len(item_types) > 0: files = filter(matches_allowed_item_types, files) if len(files): authTokens = self.get_files(backupUDID, snapshot, files) self.authorize_get(authTokens, snapshot) if self.itunes_style: self.write_info_plist(mbsbackup, snapshot)
def download(self,backupUDID,fast): mbsbackup = self.getBackup(backupUDID) print "Downloading backup %s" % backupUDID.encode("hex") self.outputFolder = os.path.join(self.outputFolder, backupUDID.encode("hex")) makedirs(self.outputFolder) #print backup_summary(mbsbackup) #print mbsbackup.Snapshot.Attributes.KeybagUUID.encode("hex") keys = self.getKeys(backupUDID) if not keys or not len(keys.Key): print "getKeys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return print "Available Snapshots: ", mbsbackup.Snapshot.SnapshotID for snapshot in xrange(1, mbsbackup.Snapshot.SnapshotID+1): print "Listing snapshot..." files = self.listFiles(backupUDID, snapshot) print "Files in snapshot %s : %s" % (snapshot,len(files)) files2 = [] if fast=='y': for f in files: if 'AddressBook.sqlitedb' in f.RelativePath or 'Calendar.sqlitedb' in f.RelativePath or 'sms.db' in f.RelativePath or 'call_history.db' in f.RelativePath or '.JPG' in f.RelativePath : files2.append(f) files = files2 if len(files): authTokens = self.getFiles(backupUDID, snapshot, files) #print authTokens self.authorizeGet(authTokens,snapshot) if fast=='n': if len(files): authTokens = self.getFiles(backupUDID, snapshot, files) #print authTokens self.authorizeGet(authTokens,snapshot)
def main(): parser = OptionParser(usage="%prog keychain.db keyfile.plist") parser.add_option("-d", "--display", dest="display", action="store_true", default=False, help="Show keychain items on stdout") parser.add_option("-s", "--sanitize", dest="sanitize", action="store_true", default=False, help="Hide secrets on stdout with ***") parser.add_option("-p", "--passwords", dest="passwords", action="store_true", default=False, help="Save generic & internet passwords as CSV file") parser.add_option("-c", "--certs", dest="certs", action="store_true", default=False, help="Extract certificates and keys") parser.add_option("-o", "--old", dest="oldpass", action="store_true", default=False, help="Bruteforce old passcodes") (options, args) = parser.parse_args() if len(args) < 2: parser.print_help() return p = plistlib.readPlist(args[1]) kb = Keybag.createWithPlist(p) k = keychain_load(args[0], kb, p["key835"].decode("hex")) if options.display: k.print_all(options.sanitize) if options.passwords: k.save_passwords() if options.certs: k.save_certs_keys() if options.oldpass: mc = k.get_managed_configuration() if not mc: print "Managed configuration not found" return print "Bruteforcing %d old passcodes" % len(mc.get("history",[])) for h in mc["history"]: p = bruteforce_old_pass(h) if p: print "Found : %s" % p else: print "Not Found"
def extract_backup(backup_path, output_path, password=""): if not os.path.exists(backup_path + "/Manifest.plist"): print "Manifest.plist not found" return manifest = readPlist(backup_path + "/Manifest.plist") info = readPlist(backup_path + "/Info.plist") for i in showinfo: print i + " : " + unicode(info.get(i, "missing")) print "Extract backup to %s ? (y/n)" % output_path if raw_input() == "n": return print "Backup is %sencrypted" % (int(not manifest["IsEncrypted"]) * "not ") if manifest["IsEncrypted"] and password == "": print "Enter backup password : "******"BackupKeyBag"): print "No BackupKeyBag in manifest, assuming iOS 3.x backup" decrypt_backup3(backup_path, output_path, password) else: mbdb = MBDB(backup_path) kb = Keybag.createWithBackupManifest(manifest, password) if not kb: return manifest["password"] = password makedirs(output_path) plistlib.writePlist(manifest, output_path + "/Manifest.plist") mbdb.keybag = kb mbdb.extract_backup(output_path) print "You can decrypt the keychain using the following command : " print "python keychain_tool.py -d %s %s" % ( output_path + "/keychain-backup.plist", output_path + "/Manifest.plist")
def __init__(self, file, **kwargs): super(EMFVolume,self).__init__(file, **kwargs) pl = "%s.plist" % self.volumeID().encode("hex") dirname = os.path.dirname(file) if dirname != "": pl = dirname + "/" + pl if not os.path.exists(pl): raise Exception("Missing keyfile %s" % pl) try: pldict = plistlib.readPlist(pl) self.emfkey = pldict["EMF"].decode("hex") self.lbaoffset = pldict["dataVolumeOffset"] self.keystore = Keybag.createWithPlist(pldict) except: raise #Exception("Invalid keyfile") rootxattr = self.getXattr(kHFSRootParentID, "com.apple.system.cprotect") if rootxattr == None: print "Not an EMF image, no root com.apple.system.cprotec xattr" else: self.cp_root = cp_root_xattr.parse(rootxattr) print "cprotect version :", self.cp_root.major_version assert self.cp_root.major_version == 2 or self.cp_root.major_version == 4
def extract_backup(backup_path, output_path, password=""): if not os.path.exists(backup_path + "/Manifest.plist"): print "Manifest.plist not found" return manifest = readPlist(backup_path + "/Manifest.plist") info = readPlist(backup_path + "/Info.plist") for i in showinfo: print i + " : " + unicode(info.get(i, "missing")) print "Extract backup to %s ? (y/n)" % output_path if raw_input() == "n": return print "Backup is %sencrypted" % (int(not manifest["IsEncrypted"]) * "not ") if manifest["IsEncrypted"] and password == "": print "Enter backup password : "******"BackupKeyBag"): print "No BackupKeyBag in manifest, assuming iOS 3.x backup" decrypt_backup3(backup_path, output_path, password) elif os.path.exists(backup_path + "/Manifest.mbdb"): mbdb = MBDB(backup_path) kb = Keybag.createWithBackupManifest(manifest, password) if not kb: return manifest["password"] = password makedirs(output_path) plistlib.writePlist(manifest, output_path + "/Manifest.plist") mbdb.keybag = kb mbdb.extract_backup(output_path) print "You can decrypt the keychain using the following command : " print "python keychain_tool.py -d \"%s\" \"%s\"" % ( output_path + "/KeychainDomain/keychain-backup.plist", output_path + "/Manifest.plist") elif os.path.exists(backup_path + "/Manifest.db"): if 'ManifestKey' in manifest: kb = Keybag.createWithBackupManifest(manifest, password, ios102=True) else: kb = Keybag.createWithBackupManifest(manifest, password) if not kb: return manifest["password"] = password makedirs(output_path) plistlib.writePlist(manifest, output_path + "/Manifest.plist") manifest_key = None if 'ManifestKey' in manifest: clas = struct.unpack('<L', manifest['ManifestKey'].data[:4])[0] wkey = manifest['ManifestKey'].data[4:] manifest_key = kb.unwrapKeyForClass(clas, wkey) manifset_db = ManifestDB(backup_path, key=manifest_key) manifset_db.keybag = kb manifset_db.extract_backup(output_path) print "You can decrypt the keychain using the following command: " print "python keychain_tool.py -d \"%s\" \"%s\"" % ( output_path + "/KeychainDomain/keychain-backup.plist", output_path + "/Manifest.plist") else: print "No Manifest database found, Is it a complete backup?"
class MobileBackupClient(object): def __init__(self, account_settings, dsPrsID, auth, outputFolder): mobilebackup_url = account_settings["com.apple.mobileme"]["com.apple.Dataclass.Backup"]["url"] content_url = account_settings["com.apple.mobileme"]["com.apple.Dataclass.Content"]["url"] self.mobilebackup_host = re.match("https://(.*):443", mobilebackup_url).group(1) self.content_host = re.match("https://(.*):443", content_url).group(1) self.dsPrsID = dsPrsID self.headers = {"Authorization": auth, "X-MMe-Client-Info": Client_Info, "User-Agent": USER_AGENT_MOBILE_BACKUP, "X-Apple-MBS-Protocol-Version": "1.7" #error 400 without this } self.headers2 = {"x-apple-mmcs-proto-version": "3.3", "x-apple-mmcs-dataclass": "com.apple.Dataclass.Backup", "x-apple-mme-dsid": str(self.dsPrsID), "User-Agent":USER_AGENT_BACKUPD, "Accept": "application/vnd.com.apple.me.ubchunk+protobuf", "Content-Type": "application/vnd.com.apple.me.ubchunk+protobuf", "x-mme-client-info": Client_Info_backup } self.files = {} self.outputFolder = outputFolder def mobileBackupRequest(self, method, url, msg=None, body=""): return probobuf_request(self.mobilebackup_host, method, url, body, self.headers, msg) def getAccount(self): return self.mobileBackupRequest("GET", "/mbs/%d" % self.dsPrsID, MBSAccount) def getBackup(self, backupUDID): return self.mobileBackupRequest("GET", "/mbs/%d/%s" % (self.dsPrsID, backupUDID.encode("hex")), MBSBackup) def getKeys(self, backupUDID): return self.mobileBackupRequest("GET", "/mbs/%d/%s/getKeys" % (self.dsPrsID, backupUDID.encode("hex")), MBSKeySet) def listFiles(self, backupUDID, snapshotId): files = self.mobileBackupRequest("GET", "/mbs/%d/%s/%d/listFiles" % (self.dsPrsID, backupUDID.encode("hex"), snapshotId)) return decode_protobuf_array(files, MBSFile) def getFiles(self, backupUDID, snapshotId, files): r = [] h = {} for f in files: if f.Size == 0: continue ff = MBSFile() ff.FileID = f.FileID h[f.FileID] = f.Signature r.append(ff) self.files[f.Signature] = f body = encode_protobuf_array(r) z = self.mobileBackupRequest("POST", "/mbs/%d/%s/%d/getFiles" % (self.dsPrsID, backupUDID.encode("hex"), snapshotId), None, body) tokens = decode_protobuf_array(z, MBSFileAuthToken) z = MBSFileAuthTokens() for t in tokens: toto = z.tokens.add() toto.FileID = h[t.FileID] #use signature toto.AuthToken = t.AuthToken return z def authorizeGet(self, tokens): self.headers2["x-apple-mmcs-auth"]= "%s %s" % (tokens.tokens[0].FileID.encode("hex"), tokens.tokens[0].AuthToken) body = tokens.SerializeToString() filegroups = probobuf_request(self.content_host, "POST", "/%d/authorizeGet" % self.dsPrsID, body, self.headers2, FileGroups) #print filegroups filechunks = {} for group in filegroups.file_groups: for container_index in xrange(len(group.storage_host_chunk_list)): data = self.downloadChunks(group.storage_host_chunk_list[container_index]) for file_ref in group.file_checksum_chunk_references: if not self.files.has_key(file_ref.file_checksum): continue decrypted_chunks = filechunks.setdefault(file_ref.file_checksum, {}) for i in xrange(len(file_ref.chunk_references)): ref = file_ref.chunk_references[i] if ref.container_index == container_index: decrypted_chunks[i] = data[ref.chunk_index] if len(decrypted_chunks) == len(file_ref.chunk_references): f = self.files[file_ref.file_checksum] self.writeFile(f, decrypted_chunks) del self.files[file_ref.file_checksum] pprint(self.files) return filegroups def getComplete(self, mmcs_auth): self.headers2["x-apple-mmcs-auth"] = mmcs_auth body = "" probobuf_request(self.content_host, "POST", "/%d/getComplete" % self.dsPrsID, body, self.headers2) def downloadChunks(self, storage_host): headers = {} for h in storage_host.host_info.headers: headers[h.name] = h.value d = probobuf_request(storage_host.host_info.hostname, storage_host.host_info.method, storage_host.host_info.uri, "", headers) decrypted = [] i = 0 for chunk in storage_host.chunk_info: decrypted.append(decrypt_chunk(d[i:i+chunk.chunk_length], chunk.chunk_encryption_key, chunk.chunk_checksum)) i += chunk.chunk_length return decrypted def writeFile(self, f, decrypted_chunks): path = os.path.join(self.outputFolder, re.sub(r'[:|*<>?"]', "_", f.RelativePath)) print path makedirs(os.path.dirname(path)) ff = open(path, "wb") h = hashlib.sha1() for i in xrange(len(decrypted_chunks)): d = decrypted_chunks[i] h.update(d) ff.write(d) ff.close() if f.Attributes.EncryptionKey: EncryptionKey = f.Attributes.EncryptionKey #ProtectionClass = f.Attributes.ProtectionClass hexdump(EncryptionKey) ProtectionClass = struct.unpack(">L", EncryptionKey[0x18:0x1C])[0] assert ProtectionClass == f.Attributes.ProtectionClass #EncryptionKeyVersion=2 => starts with keybag uuid if f.Attributes.EncryptionKeyVersion and f.Attributes.EncryptionKeyVersion == 2: assert self.kb.uuid == EncryptionKey[:0x10] keyLength = struct.unpack(">L", EncryptionKey[0x20:0x24])[0] assert keyLength == 0x48 wrapped_key = EncryptionKey[0x24:] else:#XXX old format ios 5 backup wrapped_key = EncryptionKey[0x1C:] print "ProtectionClass= %d" % ProtectionClass filekey = self.kb.unwrapCurve25519(ProtectionClass, wrapped_key) if not filekey: print "Failed to unwrap file key for file %s !!!" % f.RelativePath else: print "filekey",filekey.encode("hex") self.decryptProtectedFile(path, filekey, f.Attributes.DecryptedSize) def decryptProtectedFile(self, path, filekey, DecryptedSize=0): ivkey = hashlib.sha1(filekey).digest()[:16] h = hashlib.sha1() sz = os.path.getsize(path) #iOS 5 trailer = uint64 sz + sha1 of encrypted file #assert (sz % 0x1000) == 0x1C oldpath = path + ".encrypted" try: os.rename(path, oldpath) except: pass f1 = open(oldpath, "rb") f2 = open(path, "wb") n = (sz / 0x1000) if DecryptedSize: n += 1 for block in xrange(n): iv = AESencryptCBC(self.computeIV(block * 0x1000), ivkey) data = f1.read(0x1000) h.update(data) f2.write(AESdecryptCBC(data, filekey, iv)) if DecryptedSize == 0: #old iOS 5 format trailer = f1.read(0x1C) DecryptedSize = struct.unpack(">Q", trailer[:8])[0] assert h.digest() == trailer[8:] f1.close() f2.truncate(DecryptedSize) f2.close() def computeIV(self, lba): iv = "" lba &= 0xffffffff for _ in xrange(4): if (lba & 1): lba = 0x80000061 ^ (lba >> 1); else: lba = lba >> 1; iv += struct.pack("<L", lba) return iv def download(self, backupUDID): mbsbackup = self.getBackup(backupUDID) print "Downloading backup %s" % backupUDID.encode("hex") self.outputFolder = os.path.join(self.outputFolder, backupUDID.encode("hex")) makedirs(self.outputFolder) print backup_summary(mbsbackup) #print mbsbackup.Snapshot.Attributes.KeybagUUID.encode("hex") keys = self.getKeys(backupUDID) if not keys or not len(keys.Key): print "getKeys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return for snapshot in xrange(1, mbsbackup.Snapshot.SnapshotID+1): files = self.listFiles(backupUDID, snapshot) print "%d files" % len(files) files2 = [] for f in files: if f.Attributes.EncryptionKey: files2.append(f) print f if len(files2): authTokens = self.getFiles(backupUDID, snapshot, files) self.authorizeGet(authTokens)
class MobileBackupClient(object): def __init__(self, account_settings, dsPrsID, auth, outputFolder): mobilebackup_url = account_settings["com.apple.mobileme"][ "com.apple.Dataclass.Backup"]["url"] content_url = account_settings["com.apple.mobileme"][ "com.apple.Dataclass.Content"]["url"] self.mobilebackup_host = re.match("https://(.*):443", mobilebackup_url).group(1) self.content_host = re.match("https://(.*):443", content_url).group(1) self.dsPrsID = dsPrsID self.headers = { "Authorization": auth, "X-MMe-Client-Info": Client_Info, "User-Agent": USER_AGENT_MOBILE_BACKUP, "X-Apple-MBS-Protocol-Version": "1.7" } self.headers2 = { "x-apple-mmcs-proto-version": "3.3", "x-apple-mmcs-dataclass": "com.apple.Dataclass.Backup", "x-apple-mme-dsid": str(self.dsPrsID), "User-Agent": USER_AGENT_BACKUPD, "Accept": "application/vnd.com.apple.me.ubchunk+protobuf", "Content-Type": "application/vnd.com.apple.me.ubchunk+protobuf", "x-mme-client-info": Client_Info_backup } self.files = {} self.outputFolder = outputFolder def mobileBackupRequest(self, method, url, msg=None, body=""): return probobuf_request(self.mobilebackup_host, method, url, body, self.headers, msg) def getAccount(self): return self.mobileBackupRequest("GET", "/mbs/%d" % self.dsPrsID, MBSAccount) def getBackup(self, backupUDID): return self.mobileBackupRequest( "GET", "/mbs/%d/%s" % (self.dsPrsID, backupUDID.encode("hex")), MBSBackup) def getKeys(self, backupUDID): return self.mobileBackupRequest( "GET", "/mbs/%d/%s/getKeys" % (self.dsPrsID, backupUDID.encode("hex")), MBSKeySet) def listFiles(self, backupUDID, snapshotId): files = self.mobileBackupRequest( "GET", "/mbs/%d/%s/%d/listFiles?offset=0&limit=100" % (self.dsPrsID, backupUDID.encode("hex"), snapshotId)) i = 100 files2 = 1 while files2: files2 = self.mobileBackupRequest( "GET", "/mbs/%d/%s/%d/listFiles?offset=%s&limit=100" % (self.dsPrsID, backupUDID.encode("hex"), snapshotId, str(i))) if files2: i = i + 100 files = files + files2 print "\tShifting offset: ", i #print files return decode_protobuf_array(files, MBSFile) def getFiles(self, backupUDID, snapshotId, files): r = [] h = {} for f in files: if f.Size == 0: continue ff = MBSFile() ff.FileID = f.FileID h[f.FileID] = f.Signature r.append(ff) self.files[f.Signature] = f body = encode_protobuf_array(r) z = self.mobileBackupRequest( "POST", "/mbs/%d/%s/%d/getFiles" % (self.dsPrsID, backupUDID.encode("hex"), snapshotId), None, body) tokens = decode_protobuf_array(z, MBSFileAuthToken) z = MBSFileAuthTokens() for t in tokens: toto = z.tokens.add() toto.FileID = h[t.FileID] toto.AuthToken = t.AuthToken return z def authorizeGet(self, tokens, snapshot): self.headers2["x-apple-mmcs-auth"] = "%s %s" % ( tokens.tokens[0].FileID.encode("hex"), tokens.tokens[0].AuthToken) body = tokens.SerializeToString() filegroups = probobuf_request(self.content_host, "POST", "/%d/authorizeGet" % self.dsPrsID, body, self.headers2, FileGroups) #print filegroups filechunks = {} for group in filegroups.file_groups: for container_index in xrange(len(group.storage_host_chunk_list)): data = self.downloadChunks( group.storage_host_chunk_list[container_index]) for file_ref in group.file_checksum_chunk_references: if not self.files.has_key(file_ref.file_checksum): continue decrypted_chunks = filechunks.setdefault( file_ref.file_checksum, {}) for i in xrange(len(file_ref.chunk_references)): ref = file_ref.chunk_references[i] if ref.container_index == container_index: decrypted_chunks[i] = data[ref.chunk_index] if len(decrypted_chunks) == len(file_ref.chunk_references): f = self.files[file_ref.file_checksum] self.writeFile(f, decrypted_chunks, snapshot) del self.files[file_ref.file_checksum] return filegroups def getComplete(self, mmcs_auth): self.headers2["x-apple-mmcs-auth"] = mmcs_auth body = "" probobuf_request(self.content_host, "POST", "/%d/getComplete" % self.dsPrsID, body, self.headers2) def downloadChunks(self, storage_host): headers = {} for h in storage_host.host_info.headers: headers[h.name] = h.value d = probobuf_request(storage_host.host_info.hostname, storage_host.host_info.method, storage_host.host_info.uri, "", headers) decrypted = [] i = 0 for chunk in storage_host.chunk_info: dchunk = decrypt_chunk(d[i:i + chunk.chunk_length], chunk.chunk_encryption_key, chunk.chunk_checksum) if dchunk: decrypted.append(dchunk) i += chunk.chunk_length return decrypted def writeFile(self, f, decrypted_chunks, snapshot): if not os.path.exists( os.path.join( self.outputFolder, re.sub(r'[:|*<>?"]', "_", "snapshot_" + str(snapshot) + "/" + f.Domain))): os.makedirs( os.path.join( self.outputFolder, re.sub(r'[:|*<>?"]', "_", "snapshot_" + str(snapshot) + "/" + f.Domain))) path = os.path.join( self.outputFolder, re.sub( r'[:|*<>?"]', "_", "snapshot_" + str(snapshot) + "/" + f.Domain + "/" + f.RelativePath)) #print '\t',f print '\t', f.Domain, '\t', path makedirs(os.path.dirname(path)) ff = open(path, "wb") h = hashlib.sha1() for i in xrange(len(decrypted_chunks)): d = decrypted_chunks[i] h.update(d) ff.write(d) ff.close() #If file is encrypted if f.Attributes.EncryptionKey: EncryptionKey = f.Attributes.EncryptionKey ProtectionClass = struct.unpack(">L", EncryptionKey[0x18:0x1C])[0] if ProtectionClass == f.Attributes.ProtectionClass: if f.Attributes.EncryptionKeyVersion and f.Attributes.EncryptionKeyVersion == 2: assert self.kb.uuid == EncryptionKey[:0x10] keyLength = struct.unpack(">L", EncryptionKey[0x20:0x24])[0] assert keyLength == 0x48 wrapped_key = EncryptionKey[0x24:] else: wrapped_key = EncryptionKey[0x1C:] filekey = self.kb.unwrapCurve25519(ProtectionClass, wrapped_key) if not filekey: print "Failed to unwrap file key for file %s !!!" % f.RelativePath else: print "\tfilekey", filekey.encode("hex") self.decryptProtectedFile(path, filekey, f.Attributes.DecryptedSize) else: print "\tUnable to decrypt file, possible old backup format", f.RelativePath #hexdump(EncryptionKey) def decryptProtectedFile(self, path, filekey, DecryptedSize=0): ivkey = hashlib.sha1(filekey).digest()[:16] h = hashlib.sha1() sz = os.path.getsize(path) #iOS 5 trailer = uint64 sz + sha1 of encrypted file #assert (sz % 0x1000) == 0x1C oldpath = path + ".encrypted" try: os.rename(path, oldpath) except: pass f1 = open(oldpath, "rb") f2 = open(path, "wb") n = (sz / 0x1000) if DecryptedSize: n += 1 for block in xrange(n): iv = AESencryptCBC(self.computeIV(block * 0x1000), ivkey) data = f1.read(0x1000) h.update(data) f2.write(AESdecryptCBC(data, filekey, iv)) if DecryptedSize == 0: #old iOS 5 format trailer = f1.read(0x1C) DecryptedSize = struct.unpack(">Q", trailer[:8])[0] assert h.digest() == trailer[8:] f1.close() f2.truncate(DecryptedSize) f2.close() def computeIV(self, lba): iv = "" lba &= 0xffffffff for _ in xrange(4): if (lba & 1): lba = 0x80000061 ^ (lba >> 1) else: lba = lba >> 1 iv += struct.pack("<L", lba) return iv def download(self, backupUDID, fast): mbsbackup = self.getBackup(backupUDID) print "Downloading backup %s" % backupUDID.encode("hex") self.outputFolder = os.path.join(self.outputFolder, backupUDID.encode("hex")) makedirs(self.outputFolder) #print backup_summary(mbsbackup) #print mbsbackup.Snapshot.Attributes.KeybagUUID.encode("hex") keys = self.getKeys(backupUDID) if not keys or not len(keys.Key): print "getKeys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return print "Available Snapshots: ", mbsbackup.Snapshot.SnapshotID for snapshot in xrange(1, mbsbackup.Snapshot.SnapshotID + 1): print "Listing snapshot..." files = self.listFiles(backupUDID, snapshot) print "Files in snapshot %s : %s" % (snapshot, len(files)) files2 = [] if fast == 'y': for f in files: if 'AddressBook.sqlitedb' in f.RelativePath or 'Calendar.sqlitedb' in f.RelativePath or 'sms.db' in f.RelativePath or 'call_history.db' in f.RelativePath or '.JPG' in f.RelativePath: files2.append(f) files = files2 if len(files): authTokens = self.getFiles(backupUDID, snapshot, files) #print authTokens self.authorizeGet(authTokens, snapshot) if fast == 'n': if len(files): authTokens = self.getFiles(backupUDID, snapshot, files) #print authTokens self.authorizeGet(authTokens, snapshot)
def main(): # Get the arguments if len(sys.argv) != 3: print "Usage: backup_passwd_guess.py iOS_Backup_Dir Password_Dictionary" sys.exit(1) backup = sys.argv[1] pwddict = sys.argv[2] # Open the manifest plist manifest_loc = backup + "/Manifest.plist" if not os.path.exists(manifest_loc): print "Can't find Manifest.plist - bad backup?" sys.exit(1) manifest = readPlist(manifest_loc) # Open the dictionary if not os.path.exists(pwddict): print "Can't find dictionary" sys.exit(1) dictfile = open(pwddict) # Get the backup information info = readPlist(backup + "/Info.plist") print "Backup Details:" print " Device: %s" % (info['Product Name']) print " Serial: %s" % (info['Serial Number']) print " Firmware: %s" % (info['Product Version']) print "" # Make sure the backup is encrypted if not manifest["IsEncrypted"]: print "Backup is not encrypted" sys.exit(1) # Determine if we have the new format of the backup encryption iosFlag = False if 'ManifestKey' in manifest: print "***** Backup is encrypted using newer algorithm. Time per try is now minutes instead of seconds *****" print "" iosFlag = True # Get the keybag kb = Keybag(manifest["BackupKeyBag"].data) kb.deviceKey = None if kb.type != BACKUP_KEYBAG and kb.type != OTA_KEYBAG: print "Backup does not contain a backup keybag" sys.exit(1) salt = kb.attrs["SALT"] iter = kb.attrs["ITER"] if iosFlag: dpsl = kb.attrs["DPSL"] dpic = kb.attrs["DPIC"] # Loop through the passwords in the file while True: password = dictfile.readline() if password == "": break password = password[:-1] opassword = password print "Trying %s" % (opassword) # Check the password if iosFlag: password = PBKDF2(password, dpsl, iterations = dpic, digestmodule=SHA256).read(32) code = PBKDF2(password, salt, iterations=iter).read(32) success = 0 for classkey in kb.classKeys.values(): k = classkey["WPKY"] if classkey["WRAP"] & WRAP_PASSCODE: k = AESUnwrap(code, classkey["WPKY"]) if not k: success = 1 break if classkey["WRAP"] & WRAP_DEVICE: if not kb.deviceKey: continue k = AESdecryptCBC(k, kb.deviceKey) if success == 0: print "Password found - ",opassword break
class MobileBackupClient(object): def __init__(self, account_settings, dsPrsID, auth, output_folder): mobilebackup_url = account_settings["com.apple.mobileme"]["com.apple.Dataclass.Backup"]["url"] content_url = account_settings["com.apple.mobileme"]["com.apple.Dataclass.Content"]["url"] self.mobilebackup_host = host_from_url(mobilebackup_url) self.content_host = host_from_url(content_url) self.dsPrsID = dsPrsID self.headers = { 'Authorization': auth, 'X-MMe-Client-Info': CLIENT_INFO, 'User-Agent': USER_AGENT_MOBILE_BACKUP, 'X-Apple-MBS-Protocol-Version': "1.7" } self.headers2 = { 'x-apple-mmcs-proto-version': "3.3", 'x-apple-mmcs-dataclass': "com.apple.Dataclass.Backup", 'x-apple-mme-dsid': str(self.dsPrsID), 'User-Agent': USER_AGENT_BACKUPD, 'Accept': "application/vnd.com.apple.me.ubchunk+protobuf", 'Content-Type': "application/vnd.com.apple.me.ubchunk+protobuf", 'x-mme-client-info': CLIENT_INFO_BACKUP } self.files = {} self.output_folder = output_folder def mobile_backup_request(self, method, url, msg=None, body=""): return probobuf_request(self.mobilebackup_host, method, url, body, self.headers, msg) def get_account(self): return self.mobile_backup_request("GET", MBS[self.dsPrsID](), MBSAccount) def get_backup(self, backupUDID): return self.mobile_backup_request("GET", MBS[self.dsPrsID][backupUDID.encode("hex")](), MBSBackup) def get_keys(self, backupUDID): return self.mobile_backup_request("GET", MBS[self.dsPrsID][backupUDID.encode("hex")].getKeys(), MBSKeySet) def list_files(self, backupUDID, snapshotId): limit = 5000 files = "" offset = 0 new_files = self.mobile_backup_request("GET", MBS[self.dsPrsID][backupUDID.encode("hex")][snapshotId]['listFiles'](offset=offset, limit=limit)) while new_files: files = files + new_files offset += limit; new_files = self.mobile_backup_request("GET", MBS[self.dsPrsID][backupUDID.encode("hex")][snapshotId](offset=offset, limit=limit)) print "\tShifting offset: ", offset return decode_protobuf_array(files, MBSFile) def get_files(self, backupUDID, snapshotId, files): r = [] h = {} for file in files: if file.Size == 0: continue ff = MBSFile() ff.FileID = file.FileID h[file.FileID] = file.Signature r.append(ff) self.files[file.Signature] = file body = encode_protobuf_array(r) z = self.mobile_backup_request("POST", MBS[self.dsPrsID][backupUDID.encode("hex")][snapshotId].getFiles(), None, body) tokens = decode_protobuf_array(z, MBSFileAuthToken) z = MBSFileAuthTokens() for token in tokens: toto = z.tokens.add() toto.FileID = h[token.FileID] toto.AuthToken = token.AuthToken return z def authorize_get(self, tokens, snapshot): self.headers2["x-apple-mmcs-auth"]= "%s %s" % (tokens.tokens[0].FileID.encode("hex"), tokens.tokens[0].AuthToken) body = tokens.SerializeToString() file_groups = probobuf_request(self.content_host, "POST", URL[self.dsPrsID].authorizeGet(), body, self.headers2, FileGroups) file_chunks = {} for group in file_groups.file_groups: for container_index, container in enumerate(group.storage_host_chunk_list): data = self.download_chunks(container) for file_ref in group.file_checksum_chunk_references: if file_ref.file_checksum not in self.files: continue decrypted_chunks = file_chunks.setdefault(file_ref.file_checksum, {}) for i, reference in enumerate(file_ref.chunk_references): if reference.container_index == container_index: decrypted_chunks[i] = data[reference.chunk_index] if len(decrypted_chunks) == len(file_ref.chunk_references): file = self.files[file_ref.file_checksum] try: self.write_file(file, decrypted_chunks, snapshot) except: raise else: del self.files[file_ref.file_checksum] return file_groups def get_complete(self, mmcs_auth): self.headers2["x-apple-mmcs-auth"] = mmcs_auth body = "" probobuf_request(self.content_host, "POST", URL[self.dsPrsID].getComplete(), body, self.headers2) def download_chunks(self, container): headers = {} # XXX for header in container.host_info.headers: headers[header.name] = header.value d = probobuf_request(container.host_info.hostname, container.host_info.method, container.host_info.uri, "", headers) decrypted = [] i = 0 for chunk in container.chunk_info: dchunk = decrypt_chunk(d[i:i+chunk.chunk_length], chunk.chunk_encryption_key, chunk.chunk_checksum) if dchunk: decrypted.append(dchunk) i += chunk.chunk_length return decrypted def write_file(self, file, decrypted_chunks, snapshot): # If the filename should be left in the iTunes backup style if self.itunes_style: if self.combined: directory = self.output_folder else: directory = os.path.join(self.output_folder, "snapshot_"+str(snapshot)) path_hash = hashlib.sha1(file.Domain+"-"+file.RelativePath).hexdigest() path = os.path.join(directory, path_hash) else: if self.combined: directory = os.path.join(self.output_folder, re.sub(r'[:|*<>?"]', "_",file.Domain)) path = os.path.join(directory, file.RelativePath) else: directory = os.path.join(self.output_folder, re.sub(r'[:|*<>?"]', "_", "snapshot_"+str(snapshot)+"/"+file.Domain)) path = os.path.join(directory, file.RelativePath) mkdir_p(os.path.dirname(path)) print '\t', file.Domain, '\t', path with open(path, "wb") as ff: hash = hashlib.sha1() for key, chunk in decrypted_chunks.iteritems(): hash.update(chunk) ff.write(chunk) # If file is encrypted if file.Attributes.EncryptionKey: key = file.Attributes.EncryptionKey ProtectionClass = struct.unpack(">L", key[0x18:0x1C])[0] if ProtectionClass == file.Attributes.ProtectionClass: if file.Attributes.EncryptionKeyVersion and file.Attributes.EncryptionKeyVersion == 2: assert self.kb.uuid == key[:0x10] keyLength = struct.unpack(">L", key[0x20:0x24])[0] assert keyLength == 0x48 wrapped_key = key[0x24:] else: wrapped_key = key[0x1C:] filekey = self.kb.unwrapCurve25519(ProtectionClass, wrapped_key) if not filekey: print "Failed to unwrap file key for file %s !!!" % file.RelativePath else: print "\tfilekey", filekey.encode("hex") self.decrypt_protected_file(path, filekey, file.Attributes.DecryptedSize) else: print "\tUnable to decrypt file, possible old backup format", file.RelativePath def decrypt_protected_file(self, path, filekey, decrypted_size=0): ivkey = hashlib.sha1(filekey).digest()[:16] hash = hashlib.sha1() sz = os.path.getsize(path) oldpath = path + ".encrypted" try: os.rename(path, oldpath) except: pass with open(oldpath, "rb") as old_file: with open(path, "wb") as new_file: n = sz / 0x1000 if decrypted_size: n += 1 for block in xrange(n): iv = AESencryptCBC(self.computeIV(block * 0x1000), ivkey) old_data = old_file.read(0x1000) hash.update(old_data) new_file.write(AESdecryptCBC(old_data, filekey, iv)) if decrypted_size == 0: #old iOS 5 format trailer = old_file.read(0x1C) decrypted_size = struct.unpack(">Q", trailer[:8])[0] assert hash.digest() == trailer[8:] new_file.truncate(decrypted_size) def computeIV(self, lba): iv = "" lba &= 0xffffffff for _ in xrange(4): if (lba & 1): lba = 0x80000061 ^ (lba >> 1); else: lba = lba >> 1; iv += struct.pack("<L", lba) return iv def download(self, backupUDID, item_types): mbsbackup = self.get_backup(backupUDID) self.output_folder = os.path.join(self.output_folder, backupUDID.encode("hex")) print "Downloading backup {} to {}".format(backupUDID.encode("hex"), self.output_folder) try: mkdir_p(self.output_folder) except OSError: print "Directory \"{}\" already exists.".format(self.output_folder) return keys = self.get_keys(backupUDID) if not keys or not len(keys.Key): print "get_keys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return print "Available Snapshots: ", mbsbackup.Snapshot.SnapshotID #for snapshot in xrange(1, mbsbackup.Snapshot.SnapshotID+1): for snapshot in [1, mbsbackup.Snapshot.SnapshotID - 1, mbsbackup.Snapshot.SnapshotID]: print "Listing snapshot..." files = self.list_files(backupUDID, snapshot) print "Files in snapshot %s : %s" % (snapshot, len(files)) def matches_allowed_item_types(file): return any(ITEM_TYPES_TO_FILE_NAMES[item_type] in file.RelativePath \ for item_type in item_types) if len(item_types) > 0: files = filter(matches_allowed_item_types, files) if len(files): authTokens = self.get_files(backupUDID, snapshot, files) self.authorize_get(authTokens, snapshot) if self.itunes_style: self.write_info_plist(mbsbackup, snapshot) # Writes a plist file in the output_directory simular to that created by iTunes during backup def write_info_plist(self, mbsbackup, snapshot): if self.combined: directory = self.output_folder else: directory = os.path.join(self.output_folder, "snapshot_"+str(snapshot)) plist_file = open(directory+"/Info.plist", "w") # TODO: Use plistlib to generate the XML plist_file.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") plist_file.write("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n") plist_file.write("<plist version=\"1.0\">\n") plist_file.write("<dict>\n") plist_file.write(" <key>Build Version</key>\n") plist_file.write(" <string>10B329</string>\n") plist_file.write(" <key>Device Name</key>\n") plist_file.write(" <string>{}</string>\n".format(mbsbackup.Attributes.DeviceClass)) plist_file.write(" <key>Display Name</key>\n") plist_file.write(" <string>{}</string>\n".format(mbsbackup.Attributes.DeviceClass)) plist_file.write(" <key>GUID</key>\n") plist_file.write(" <string></string>\n") plist_file.write(" <key>IMEI</key>\n") plist_file.write(" <string></string>\n") plist_file.write(" <key>Product Type</key>\n") plist_file.write(" <string>{}</string>\n".format(mbsbackup.Attributes.HardwareModel)) plist_file.write(" <key>Product Version</key>\n") plist_file.write(" <string>6.1.3</string>\n") plist_file.write(" <key>Serial Number</key>\n") plist_file.write(" <string></string>\n") plist_file.write(" <key>Target Identifier</key>\n") plist_file.write(" <string></string>\n") plist_file.write(" <key>Target Type</key>\n") plist_file.write(" <string>Device</string>\n") plist_file.write(" <key>Unique Identifier</key>\n") plist_file.write(" <string></string>\n") plist_file.write(" <key>iTunes Settings</key>\n") plist_file.write(" <dict/>\n") plist_file.write(" <key>iTunes Version</key>\n") plist_file.write(" <string>11.1</string>\n") plist_file.write("</dict>\n") plist_file.write("</plist>\n") plist_file.close()
def main(): parser = OptionParser(usage="%prog keychain.db/keychain-backup.plist keyfile.plist/Manifest.plist") parser.add_option("-d", "--display", dest="display", action="store_true", default=False, help="Show keychain items on stdout") parser.add_option("-s", "--sanitize", dest="sanitize", action="store_true", default=False, help="Hide secrets on stdout with ***") parser.add_option("-p", "--passwords", dest="passwords", action="store_true", default=False, help="Save generic & internet passwords as CSV file") parser.add_option("-c", "--certs", dest="certs", action="store_true", default=False, help="Extract certificates and keys") parser.add_option("-o", "--old", dest="oldpass", action="store_true", default=False, help="Bruteforce old passcodes") (options, args) = parser.parse_args() if len(args) < 2: parser.print_help() return p = readPlist(args[1]) if p.has_key("BackupKeyBag"): deviceKey = None if p.has_key("key835"): deviceKey = p["key835"].decode("hex") else: if not p["IsEncrypted"]: print "This backup is not encrypted, without key 835 nothing in the keychain can be decrypted" print "If you have key835 for device %s enter it (in hex)" % p["Lockdown"]["UniqueDeviceID"] d = raw_input() if len(d) == 32: p["key835"] = d deviceKey = d.decode("hex") plistlib.writePlist(p, args[1]) kb = Keybag.createWithBackupManifest(p, p.get("password",""), deviceKey) if not kb: return k = Keychain4(args[0], kb) else: kb = Keybag.createWithPlist(p) k = keychain_load(args[0], kb, p["key835"].decode("hex")) if options.display: k.print_all(options.sanitize) if options.passwords: k.save_passwords() if options.certs: k.save_certs_keys() if options.oldpass: mc = k.get_managed_configuration() if not mc: print "Managed configuration not found" return print "Bruteforcing %d old passcodes" % len(mc.get("history",[])) for h in mc["history"]: p = bruteforce_old_pass(h) if p: print "Found : %s" % p else: print "Not Found"
def bf_system(): curdir = os.path.dirname(os.path.abspath(__file__)) client = RamdiskToolClient() di = client.getDeviceInfos() devicedir = di["udid"] if os.getcwd().find(devicedir) == -1: try: os.mkdir(devicedir) except: pass os.chdir(devicedir) key835 = di.get("key835").decode("hex") systembag = client.getSystemKeyBag() kbkeys = systembag["KeyBagKeys"].data kb = Keybag.createWithDataSignBlob(kbkeys, key835) keybags = di.setdefault("keybags", {}) kbuuid = kb.uuid.encode("hex") print "Keybag UUID :", kbuuid if True and keybags.has_key(kbuuid) and keybags[kbuuid].has_key( "passcodeKey"): print "We've already seen this keybag" passcodeKey = keybags[kbuuid].get("passcodeKey").decode("hex") print kb.unlockWithPasscodeKey(passcodeKey) kb.printClassKeys() else: keybags[kbuuid] = {"KeyBagKeys": systembag["KeyBagKeys"]} di["KeyBagKeys"] = systembag["KeyBagKeys"] di.save() print "Enter passcode or leave blank for bruteforce:" z = raw_input() res = client.getPasscodeKey(systembag["KeyBagKeys"].data, z) if kb.unlockWithPasscodeKey(res.get("passcodeKey").decode("hex")): print "Passcode \"%s\" OK" % z di.update(res) keybags[kbuuid].update(res) di.save() keychain_blob = client.downloadFile( "/mnt2/Keychains/keychain-2.db") write_file("keychain-2.db", keychain_blob) print "Downloaded keychain database, use keychain_tool.py to decrypt secrets" return if z != "": print "Wrong passcode, trying to bruteforce !" if checkPasscodeComplexity(client) == 0: print "Trying all 4-digits passcodes..." bf = client.bruteforceKeyBag(systembag["KeyBagKeys"].data) if bf: di.update(bf) keybags[kbuuid].update(bf) print bf print kb.unlockWithPasscodeKey(bf.get("passcodeKey").decode("hex")) kb.printClassKeys() di["classKeys"] = kb.getClearClassKeysDict() di.save() else: print "Complex passcode used, trying dictionary attack ..." dictfile = os.path.join(curdir, 'wordlist.dict') try: wordlist = open(dictfile, 'r').readlines() except (OSError, IOError), e: exit(e) for line in wordlist: res = client.getPasscodeKey(systembag["KeyBagKeys"].data, line.rstrip('\n')) if kb.unlockWithPasscodeKey( res.get("passcodeKey").decode("hex")): print "Passcode \"%s\" OK" % line.rstrip('\n') di.update(res) keybags[kbuuid].update(res) di.save() keychain_blob = client.downloadFile( "/mnt2/Keychains/keychain-2.db") write_file("keychain-2.db", keychain_blob) print "Downloaded keychain database, use keychain_tool.py to decrypt secrets" return print "Passcode not found!" return
def download(self, backupUDID, item_types): mbsbackup = self.get_backup(backupUDID) self.output_folder = os.path.join(self.output_folder, backupUDID.encode("hex")) print "Downloading backup {} to {}".format(backupUDID.encode("hex"), self.output_folder) try: mkdir_p(self.output_folder) except OSError: print "Directory \"{}\" already exists.".format(self.output_folder) return keys = self.get_keys(backupUDID) if not keys or not len(keys.Key): print "get_keys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return print "Available Snapshots: %d" % (mbsbackup.Snapshot.SnapshotID) if self.chosen_snapshot_id == None: snapshot_list = [1, mbsbackup.Snapshot.SnapshotID - 1, mbsbackup.Snapshot.SnapshotID] elif self.chosen_snapshot_id < 0: snapshot_list = [mbsbackup.Snapshot.SnapshotID + self.chosen_snapshot_id + 1] # Remember chosen_snapshot_id is negative else: snapshot_list = [self.chosen_snapshot_id] for snapshot in snapshot_list: print "Listing snapshot %d..." % (snapshot) files = self.list_files(backupUDID, snapshot) print "Files in snapshot %d" % (len(files)) def matches_allowed_domain(a_file): return self.domain_filter in a_file.Domain def matches_allowed_item_types(a_file): return any(ITEM_TYPES_TO_FILE_NAMES[item_type] in a_file.RelativePath.lower() \ for item_type in item_types) if self.domain_filter: files = filter(matches_allowed_domain, files) if len(item_types) > 0: files = filter(matches_allowed_item_types, files) print "Downloading %d files due to filter" % (len(files)) if len(files): authTokens = self.get_files(backupUDID, snapshot, files) if len(authTokens.tokens) > 0: self.authorize_get(authTokens, snapshot) if self.itunes_style: self.write_info_plist(mbsbackup, snapshot) self.write_manifest_mbdb(snapshot) else: print "Unable to download snapshot. This snapshot may not have finished uploading yet." # Clean up self.files if not self.combined : self.downloaded_files = []
def download(self, backupUDID, item_types): mbsbackup = self.get_backup(backupUDID) self.output_folder = os.path.join(self.output_folder, backupUDID.encode("hex")) print "Downloading backup {} to {}".format(backupUDID.encode("hex"), self.output_folder) try: mkdir_p(self.output_folder) except OSError: print "Directory \"{}\" already exists.".format(self.output_folder) return keys = self.get_keys(backupUDID) if not keys or not len(keys.Key): print "get_keys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[-1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return print "Available Snapshots: %d" % (mbsbackup.Snapshot.SnapshotID) if self.chosen_snapshot_id == None: snapshot_list = [ 1, mbsbackup.Snapshot.SnapshotID - 1, mbsbackup.Snapshot.SnapshotID ] elif self.chosen_snapshot_id < 0: snapshot_list = [ mbsbackup.Snapshot.SnapshotID + self.chosen_snapshot_id + 1 ] # Remember chosen_snapshot_id is negative else: snapshot_list = [self.chosen_snapshot_id] for snapshot in snapshot_list: print "Listing snapshot %d..." % (snapshot) files = self.list_files(backupUDID, snapshot) print "Files in snapshot %d" % (len(files)) def matches_allowed_domain(a_file): return self.domain_filter in a_file.Domain def matches_allowed_item_types(a_file): return any(ITEM_TYPES_TO_FILE_NAMES[item_type] in a_file.RelativePath.lower() \ for item_type in item_types) if self.domain_filter: files = filter(matches_allowed_domain, files) if len(item_types) > 0: files = filter(matches_allowed_item_types, files) print "Downloading %d files due to filter" % (len(files)) if len(files): authTokens = self.get_files(backupUDID, snapshot, files) if len(authTokens.tokens) > 0: self.authorize_get(authTokens, snapshot) if self.itunes_style: self.write_info_plist(mbsbackup, snapshot) self.write_manifest_mbdb(snapshot) else: print "Unable to download snapshot. This snapshot may not have finished uploading yet." # Clean up self.files if not self.combined: self.downloaded_files = []
def openBackup(self, path=None): if path is not None: self.backuppath = path self.manifest = readManifest(self.backuppath) if self.manifest is None: self.informationMessage("%s seems not to be a valid backup directory" % self.backuppath) self.ui.statusbar.showMessage("Stop...") return False else: # refresh gridLayoutManifest self.gridLayoutManifest_refresh(self.manifest) self.infoplist = readInfo(self.backuppath) if self.infoplist is None: self.informationMessage("Can't find Info.plist in directory %s. Not a valid backup directory?" % self.backuppath) self.ui.statusbar.showMessage("Stop...") return False else: # refresh gridLayoutInfo self.gridLayoutInfo_refresh(self.infoplist) if not self.manifest.has_key("BackupKeyBag"): self.informationMessage("Only iOSBackup >= Version 5 Supported") if self.manifest["IsEncrypted"]: self.passwd = self.passwordDialog() if self.passwd is None: self.informationMessage("Password not given! Will not be able to extract information...") progressBar = QtGui.QProgressBar() self.ui.statusbar.addWidget(progressBar) self.mbdb = MBDB(self.backuppath) if self.passwd is not None: kb = Keybag.createWithBackupManifest(self.manifest, self.passwd) if not kb: self.informationMessage( "Can not extract backup key.\nYou can only browse through the domains and apps...") # return False self.manifest["password"] = self.passwd self.mbdb.keybag = kb progressBar.setMaximum(self.mbdb.numoffiles) if TestMode is True: self.database, self.cursor = iOSBackupDB(TestDatabase) else: self.database, self.cursor = iOSBackupDB() store2db(self.cursor, self.mbdb) self.database.commit() self.ui.treeViewDomains.setHeaderLabel("Files/Domains/Apps") standardFiles = QtGui.QTreeWidgetItem(None) standardFiles.setText(0, "Standard files") self.ui.treeViewDomains.addTopLevelItem(standardFiles) for elementName in ['Manifest.plist', 'Info.plist', 'Status.plist']: newItem = QtGui.QTreeWidgetItem(standardFiles) newItem.setText(0, elementName) newItem.setText(1, "X") self.ui.treeViewDomains.addTopLevelItem(newItem) self.cursor.execute("SELECT DISTINCT(mbdomain_type) FROM indice") domain_types = self.cursor.fetchall() for domain_type_u in domain_types: domain_type = str(domain_type_u[0]) newDomainFamily = QtGui.QTreeWidgetItem(None) newDomainFamily.setText(0, domain_type) self.ui.treeViewDomains.addTopLevelItem(newDomainFamily) # show new domain family in main view QtGui.QApplication.processEvents() query = "SELECT DISTINCT(mbapp_name) FROM indice WHERE mbdomain_type = ? ORDER BY mbdomain_type" self.cursor.execute(query, (domain_type,)) domain_names = self.cursor.fetchall() for domain_name_u in domain_names: domain_name = str(domain_name_u[0]) if (len(domain_names) > 1): newDomain = QtGui.QTreeWidgetItem(newDomainFamily) newDomain.setText(0, domain_name) self.ui.treeViewDomains.addTopLevelItem(newDomain) rootNode = newDomain else: rootNode = newDomainFamily # query = "SELECT path, mbfile_path, mbfile_name, size, fileid, mbfile_type FROM indice WHERE mbdomain_type = ? AND mbapp_name = ? ORDER BY mbfile_path, mbfile_name" query = "SELECT mbfile_path, mbfile_name, size, id, mbfile_type FROM indice WHERE mbdomain_type = ? AND mbapp_name = ? ORDER BY mbfile_path, mbfile_name" self.cursor.execute(query, (domain_type, domain_name)) nodes = self.cursor.fetchall() pathToNode = {'': rootNode} for nodeData in nodes: path = str(nodeData[0]) # finding parent directory lookup = path missing = collections.deque() dirNode = None while dirNode is None: dirNode = pathToNode.get(lookup, None) if dirNode is None: lookup, sep, component = lookup.rpartition('/') missing.appendleft(component) # creating parent directory if neccesary for component in missing: newPath = QtGui.QTreeWidgetItem(dirNode) newPath.setText(0, component) newPath.setToolTip(0, component) self.ui.treeViewDomains.addTopLevelItem(newPath) dirNode = newPath #lookup = posixpath.join(lookup, component) lookup = path pathToNode[lookup] = newPath try: file_name = str(nodeData[1].encode("utf-8")) except: file_name = nodeData[1] if (nodeData[2]) < 1024: file_dim = str(nodeData[2]) + " b" else: file_dim = str(nodeData[2] / 1024) + " kb" file_id = int(nodeData[3]) file_type = str(nodeData[4]) if file_type == 'd': newFile = dirNode else: newFile = QtGui.QTreeWidgetItem(newPath) self.ui.treeViewDomains.addTopLevelItem(newFile) newFile.setText(0, file_name) newFile.setToolTip(0, file_name) newFile.setText(2, str(file_dim)) newFile.setText(1, file_type) newFile.setText(3, str(file_id)) newFile.setText(4, domain_type) newFile.setText(5, domain_name) rawFiles = QtGui.QTreeWidgetItem(None) rawFiles.setText(0, "Raw files") self.ui.treeViewDomains.addTopLevelItem(rawFiles) # query = "SELECT mbfile_path, mbfile_name, size, id, mbfile_type FROM indice ORDER BY mbfile_path, mbfile_name" query = "SELECT domain, path, size, id, mbfile_type FROM indice ORDER BY domain, path" self.cursor.execute(query) nodes = self.cursor.fetchall() for nodeData in nodes: domain_name = str(nodeData[0]).replace("-", "/", 1) + "/" + str(nodeData[1]) newFile = QtGui.QTreeWidgetItem(rawFiles) self.ui.treeViewDomains.addTopLevelItem(newFile) if (nodeData[2]) < 1024: file_dim = str(nodeData[2]) + " b" else: file_dim = str(nodeData[2] / 1024) + " kb" file_id = int(nodeData[3]) file_type = str(nodeData[4]) newFile.setText(0, domain_name) newFile.setToolTip(0, domain_name) newFile.setText(2, str(file_dim)) newFile.setText(1, file_type) newFile.setText(3, str(file_id)) self.ui.statusbar.removeWidget(progressBar) self.activateMenu(True) return True
def main(): key = "" keyfile = "" getkey = 0 opts,args = getopt.getopt(sys.argv[1:],"",["keyfile=","getkey"]); for o,a in opts: if o in ("--keyfile"): keyfile=a pass elif o in ("--getkey"): getkey=1 pass args.insert(0,sys.argv[0]) sys.argv=args if len(sys.argv)!=2: print "Usage: extract_encfiles.py (--keyfile keyfile) (--getkey) backupdir" sys.exit(0) backup_path = sys.argv[1] manifest = readPlist(backup_path + "/Manifest.plist") kb = Keybag(manifest["BackupKeyBag"].data) if not keyfile or getkey: password=getpass.getpass() else: password = '' if password: key=kb.getPasscodekeyFromPasscode(password,ios102=True) if getkey: if (len(key)==32): if keyfile: f=open(keyfile,"w") f.write(base64.b64encode(key)) f.close() else: print base64.b64encode(key) pass else: print "There was a problem extracting the key." pass sys.exit(0) pass f=open(keyfile,"r") key=base64.b64decode(f.read()) key=key[0:32]; kb.unlockWithPasscodeKey(key) manifest["password"] = password manifest_key = None if 'ManifestKey' in manifest: clas = struct.unpack('<L', manifest['ManifestKey'].data[:4])[0] wkey = manifest['ManifestKey'].data[4:] manifest_key = kb.unwrapKeyForClass(clas, wkey) manifest_db = ManifestDB(backup_path, key=manifest_key) manifest_db.keybag = kb for filename, mbfile in manifest_db.files.iteritems(): if (mbfile.domain=="HomeDomain"): if mbfile.relative_path in ("Library/SMS/sms.db","Library/AddressBook/AddressBook.sqlitedb"): mbfile.domain="." mbfile.relative_path=os.path.basename(mbfile.relative_path) if mbfile.relative_path=="sms.db": mbfile.relative_path="sms-1.db" pass if mbfile.relative_path=="AddressBook.sqlitedb": mbfile.relative_path="addresses.db" pass manifest_db._extract_file(filename,mbfile,"sms")
class MobileBackupClient(object): def __init__(self, account_settings, dsPrsID, auth, output_folder): mobilebackup_url = account_settings["com.apple.mobileme"]["com.apple.Dataclass.Backup"]["url"] content_url = account_settings["com.apple.mobileme"]["com.apple.Dataclass.Content"]["url"] self.mobilebackup_host = host_from_url(mobilebackup_url) self.content_host = host_from_url(content_url) self.dsPrsID = dsPrsID self.headers = { 'Authorization': auth, 'X-MMe-Client-Info': CLIENT_INFO, 'User-Agent': USER_AGENT_MOBILE_BACKUP, 'X-Apple-MBS-Protocol-Version': "1.7" } self.headers2 = { 'x-apple-mmcs-proto-version': "3.3", 'x-apple-mmcs-dataclass': "com.apple.Dataclass.Backup", 'x-apple-mme-dsid': str(self.dsPrsID), 'User-Agent': USER_AGENT_BACKUPD, 'Accept': "application/vnd.com.apple.me.ubchunk+protobuf", 'Content-Type': "application/vnd.com.apple.me.ubchunk+protobuf", 'x-mme-client-info': CLIENT_INFO_BACKUP } self.files = {} self.output_folder = output_folder self.chosen_snapshot_id = None self.combined = False self.itunes_style = False self.downloaded_files = [] self.domain_filter = None def mobile_backup_request(self, method, url, msg=None, body=""): return probobuf_request(self.mobilebackup_host, method, url, body, self.headers, msg) def get_account(self): return self.mobile_backup_request("GET", MBS[self.dsPrsID](), MBSAccount) def get_backup(self, backupUDID): return self.mobile_backup_request("GET", MBS[self.dsPrsID][backupUDID.encode("hex")](), MBSBackup) def get_keys(self, backupUDID): return self.mobile_backup_request("GET", MBS[self.dsPrsID][backupUDID.encode("hex")].getKeys(), MBSKeySet) def list_files(self, backupUDID, snapshotId): limit = 5000 files = "" offset = 0 new_files = self.mobile_backup_request("GET", MBS[self.dsPrsID][backupUDID.encode("hex")][snapshotId].listFiles(offset=offset, limit=limit)) while new_files: files = files + new_files offset += limit new_files = self.mobile_backup_request("GET", MBS[self.dsPrsID][backupUDID.encode("hex")][snapshotId].listFiles(offset=offset, limit=limit)) print "\tShifting offset: ", offset return decode_protobuf_array(files, MBSFile) def get_files(self, backupUDID, snapshotId, files): r = [] h = {} for file in files: if file.Size == 0: continue ff = MBSFile() ff.FileID = file.FileID h[file.FileID] = file.Signature r.append(ff) self.files[file.Signature] = file body = encode_protobuf_array(r) z = self.mobile_backup_request("POST", MBS[self.dsPrsID][backupUDID.encode("hex")][snapshotId].getFiles(), None, body) tokens = decode_protobuf_array(z, MBSFileAuthToken) z = MBSFileAuthTokens() for token in tokens: toto = z.tokens.add() toto.FileID = h[token.FileID] toto.AuthToken = token.AuthToken return z def authorize_get(self, tokens, snapshot): if len(tokens.tokens) == 0: return self.headers2["x-apple-mmcs-auth"]= "%s %s" % (tokens.tokens[0].FileID.encode("hex"), tokens.tokens[0].AuthToken) body = tokens.SerializeToString() file_groups = probobuf_request(self.content_host, "POST", URL[self.dsPrsID].authorizeGet(), body, self.headers2, FileGroups) file_chunks = {} for group in file_groups.file_groups: for container_index, container in enumerate(group.storage_host_chunk_list): data = self.download_chunks(container) for file_ref in group.file_checksum_chunk_references: if file_ref.file_checksum not in self.files: continue decrypted_chunks = file_chunks.setdefault(file_ref.file_checksum, {}) for i, reference in enumerate(file_ref.chunk_references): if reference.container_index == container_index: decrypted_chunks[i] = data[reference.chunk_index] if len(decrypted_chunks) == len(file_ref.chunk_references): file = self.files[file_ref.file_checksum] try: self.write_file(file, decrypted_chunks, snapshot) except: raise else: # With iTunes style we need to keep the file if self.itunes_style : self.downloaded_files.append(file) del self.files[file_ref.file_checksum] return file_groups def get_complete(self, mmcs_auth): self.headers2["x-apple-mmcs-auth"] = mmcs_auth body = "" probobuf_request(self.content_host, "POST", URL[self.dsPrsID].getComplete(), body, self.headers2) def download_chunks(self, container): headers = {} # XXX for header in container.host_info.headers: headers[header.name] = header.value d = probobuf_request(container.host_info.hostname, container.host_info.method, container.host_info.uri, "", headers) decrypted = [] i = 0 for chunk in container.chunk_info: dchunk = decrypt_chunk(d[i:i+chunk.chunk_length], chunk.chunk_encryption_key, chunk.chunk_checksum) if dchunk: decrypted.append(dchunk) i += chunk.chunk_length return decrypted def write_file(self, file, decrypted_chunks, snapshot): # If the filename should be left in the iTunes backup style if self.itunes_style: if self.combined: directory = self.output_folder else: directory = os.path.join(self.output_folder, "snapshot_"+str(snapshot)) path_hash = hashlib.sha1(file.Domain.encode('utf-8')+"-"+file.RelativePath.encode('utf-8')).hexdigest() path = os.path.join(directory, path_hash) else: if self.combined: directory = os.path.join(self.output_folder, re.sub(r'[:|*<>?"]', "_", file.Domain)) path = os.path.join(directory, file.RelativePath) else: directory = os.path.join(self.output_folder, re.sub(r'[:|*<>?"]', "_", "snapshot_"+str(snapshot)+"/"+file.Domain)) path = os.path.join(directory, file.RelativePath) mkdir_p(os.path.dirname(path)) print '\t', file.Domain, '\t', path with open(path, "wb") as ff: hash = hashlib.sha1() for key, chunk in decrypted_chunks.iteritems(): hash.update(chunk) ff.write(chunk) # If file is encrypted if file.Attributes.EncryptionKey: key = file.Attributes.EncryptionKey ProtectionClass = struct.unpack(">L", key[0x18:0x1C])[0] if ProtectionClass == file.Attributes.ProtectionClass: wrapped_key = None filekey = None if file.Attributes.EncryptionKeyVersion and file.Attributes.EncryptionKeyVersion == 2: if self.kb.uuid == key[:0x10]: keyLength = struct.unpack(">L", key[0x20:0x24])[0] if keyLength == 0x48: wrapped_key = key[0x24:] else: wrapped_key = key[0x1C:] if wrapped_key: filekey = self.kb.unwrapCurve25519(ProtectionClass, wrapped_key) if not filekey: print "Failed to unwrap file key for file %s !!!" % file.RelativePath else: print "\tfilekey", filekey.encode("hex") self.decrypt_protected_file(path, filekey, file.Attributes.DecryptedSize) else: print "\tUnable to decrypt file, possible old backup format", file.RelativePath def decrypt_protected_file(self, path, filekey, decrypted_size=0): ivkey = hashlib.sha1(filekey).digest()[:16] hash = hashlib.sha1() sz = os.path.getsize(path) oldpath = path + ".encrypted" try: os.rename(path, oldpath) except: pass with open(oldpath, "rb") as old_file: with open(path, "wb") as new_file: n = sz / 0x1000 if decrypted_size: n += 1 for block in xrange(n): iv = AESencryptCBC(self.computeIV(block * 0x1000), ivkey) old_data = old_file.read(0x1000) hash.update(old_data) new_file.write(AESdecryptCBC(old_data, filekey, iv)) if decrypted_size == 0: #old iOS 5 format trailer = old_file.read(0x1C) decrypted_size = struct.unpack(">Q", trailer[:8])[0] assert hash.digest() == trailer[8:] new_file.truncate(decrypted_size) os.remove(oldpath) # Delete the encrypted file def computeIV(self, lba): iv = "" lba &= 0xffffffff for _ in xrange(4): if (lba & 1): lba = 0x80000061 ^ (lba >> 1) else: lba = lba >> 1 iv += struct.pack("<L", lba) return iv def download(self, backupUDID, item_types): mbsbackup = self.get_backup(backupUDID) self.output_folder = os.path.join(self.output_folder, backupUDID.encode("hex")) print "Downloading backup {} to {}".format(backupUDID.encode("hex"), self.output_folder) try: mkdir_p(self.output_folder) except OSError: print "Directory \"{}\" already exists.".format(self.output_folder) return keys = self.get_keys(backupUDID) if not keys or not len(keys.Key): print "get_keys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return print "Available Snapshots: %d" % (mbsbackup.Snapshot.SnapshotID) if self.chosen_snapshot_id == None: snapshot_list = [1, mbsbackup.Snapshot.SnapshotID - 1, mbsbackup.Snapshot.SnapshotID] elif self.chosen_snapshot_id < 0: snapshot_list = [mbsbackup.Snapshot.SnapshotID + self.chosen_snapshot_id + 1] # Remember chosen_snapshot_id is negative else: snapshot_list = [self.chosen_snapshot_id] for snapshot in snapshot_list: print "Listing snapshot %d..." % (snapshot) files = self.list_files(backupUDID, snapshot) print "Files in snapshot %d" % (len(files)) def matches_allowed_domain(a_file): return self.domain_filter in a_file.Domain def matches_allowed_item_types(a_file): return any(ITEM_TYPES_TO_FILE_NAMES[item_type] in a_file.RelativePath.lower() \ for item_type in item_types) if self.domain_filter: files = filter(matches_allowed_domain, files) if len(item_types) > 0: files = filter(matches_allowed_item_types, files) print "Downloading %d files due to filter" % (len(files)) if len(files): authTokens = self.get_files(backupUDID, snapshot, files) if len(authTokens.tokens) > 0: self.authorize_get(authTokens, snapshot) if self.itunes_style: self.write_info_plist(mbsbackup, snapshot) self.write_manifest_mbdb(snapshot) else: print "Unable to download snapshot. This snapshot may not have finished uploading yet." # Clean up self.files if not self.combined : self.downloaded_files = [] # Writes a plist file in the output_directory simular to that created by iTunes during backup def write_info_plist(self, mbsbackup, snapshot): if self.combined: directory = self.output_folder else: directory = os.path.join(self.output_folder, "snapshot_"+str(snapshot)) info_plist = { "Device Name" : mbsbackup.Attributes.DeviceClass, "Display Name" : mbsbackup.Attributes.DeviceClass, "Product Type" : mbsbackup.Attributes.ProductType, "Serial Number" : mbsbackup.Attributes.SerialNumber, "Target Type" : "Device", "iTunes Version" : "11.1", "Product Version" : "8.1.1", # Must be higher than 4.0, current iTunes backup sets to 8.1.1 "Target Identifier" : mbsbackup.backupUDID.encode("hex"), "Unique Identifier" : mbsbackup.backupUDID.encode("hex") } with open(directory+"/Info.plist", 'wb') as fp: plistlib.writePlist(info_plist, fp) def write_manifest_mbdb(self, snapshot): if self.combined: directory = self.output_folder else: directory = os.path.join(self.output_folder, "snapshot_"+str(snapshot)) filename = os.path.join(directory, "Manifest.mbdb") # Generate the bare minimum MBDB file # Open file mbdb_file = open(filename, "wb") # Write file header mbdb_file.write("mbdb") mbdb_file.write("\x00\x00") # For each file for file in self.downloaded_files: # Write App Domain length mbdb_file.write( struct.pack('>h', len(file.Domain)) ) # Write App Domain mbdb_file.write( file.Domain ) # Write iPhone Filename length mbdb_file.write( struct.pack('>h', len(file.RelativePath)) ) # Write iPhone Filename mbdb_file.write( file.RelativePath ) # Write 0xFFFF for Link Target Length signifying that it is not present mbdb_file.write("\xFF\xFF") # Write 0xFFFF for SHAChecksum Length signifying the checksum is not present mbdb_file.write("\xFF\xFF") # Write 0xFFFF for the length of some unknown value, signifying it is not present mbdb_file.write("\xFF\xFF") # Write 0x27 bytes of 0x00, for the file properties as set by the iPhone during restore mbdb_file.write("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") # Write 0x00 for extended property count mbdb_file.write("\x00") # Close file mbdb_file.close()
def main(): parser = OptionParser(usage="%prog keychain.db keyfile.plist") parser.add_option("-d", "--display", dest="display", action="store_true", default=False, help="Show keychain items on stdout") parser.add_option("-s", "--sanitize", dest="sanitize", action="store_true", default=False, help="Hide secrets on stdout with ***") parser.add_option("-p", "--passwords", dest="passwords", action="store_true", default=False, help="Save generic & internet passwords as CSV file") parser.add_option("-c", "--certs", dest="certs", action="store_true", default=False, help="Extract certificates and keys") parser.add_option("-o", "--old", dest="oldpass", action="store_true", default=False, help="Bruteforce old passcodes") (options, args) = parser.parse_args() if len(args) < 2: parser.print_help() return p = plistlib.readPlist(args[1]) kb = Keybag.createWithPlist(p) k = keychain_load(args[0], kb, p["key835"].decode("hex")) if options.display: k.print_all(options.sanitize) if options.passwords: k.save_passwords() if options.certs: k.save_certs_keys() if options.oldpass: mc = k.get_managed_configuration() if not mc: print "Managed configuration not found" return print "Bruteforcing %d old passcodes" % len(mc.get("history", [])) for h in mc["history"]: p = bruteforce_old_pass(h) if p: print "Found : %s" % p else: print "Not Found"
class MobileBackupClient(object): def __init__(self, account_settings, dsPrsID, auth, output_folder): mobilebackup_url = account_settings["com.apple.mobileme"][ "com.apple.Dataclass.Backup"]["url"] content_url = account_settings["com.apple.mobileme"][ "com.apple.Dataclass.Content"]["url"] self.mobilebackup_host = host_from_url(mobilebackup_url) self.content_host = host_from_url(content_url) self.dsPrsID = dsPrsID self.headers = { 'Authorization': auth, 'X-MMe-Client-Info': CLIENT_INFO, 'User-Agent': USER_AGENT_MOBILE_BACKUP, 'X-Apple-MBS-Protocol-Version': "1.7" } self.headers2 = { 'x-apple-mmcs-proto-version': "3.3", 'x-apple-mmcs-dataclass': "com.apple.Dataclass.Backup", 'x-apple-mme-dsid': str(self.dsPrsID), 'User-Agent': USER_AGENT_BACKUPD, 'Accept': "application/vnd.com.apple.me.ubchunk+protobuf", 'Content-Type': "application/vnd.com.apple.me.ubchunk+protobuf", 'x-mme-client-info': CLIENT_INFO_BACKUP } self.files = {} self.output_folder = output_folder self.chosen_snapshot_id = None self.combined = False self.itunes_style = False self.downloaded_files = [] self.domain_filter = None self.threads = DEFAULT_THREADS def mobile_backup_request(self, method, url, msg=None, body=""): return probobuf_request(self.mobilebackup_host, method, url, body, self.headers, msg) def get_account(self): return self.mobile_backup_request("GET", MBS[self.dsPrsID](), MBSAccount) def get_backup(self, backupUDID): return self.mobile_backup_request( "GET", MBS[self.dsPrsID][backupUDID.encode("hex")](), MBSBackup) def get_keys(self, backupUDID): return self.mobile_backup_request( "GET", MBS[self.dsPrsID][backupUDID.encode("hex")].getKeys(), MBSKeySet) def list_files(self, backupUDID, snapshotId): limit = 5000 files = "" offset = 0 new_files = self.mobile_backup_request( "GET", MBS[self.dsPrsID][backupUDID.encode("hex")][snapshotId] ['listFiles'](offset=offset, limit=limit)) while new_files: files = files + new_files offset += limit new_files = self.mobile_backup_request( "GET", MBS[self.dsPrsID][backupUDID.encode("hex")] [snapshotId].listFiles(offset=offset, limit=limit)) print "\tShifting offset: ", offset return decode_protobuf_array(files, MBSFile) def get_files(self, backupUDID, snapshotId, files): r = [] h = {} for file in files: if file.Size == 0: continue ff = MBSFile() ff.FileID = file.FileID h[file.FileID] = file.Signature r.append(ff) self.files[file.Signature] = file body = encode_protobuf_array(r) z = self.mobile_backup_request( "POST", MBS[self.dsPrsID][backupUDID.encode("hex")][snapshotId].getFiles(), None, body) tokens = decode_protobuf_array(z, MBSFileAuthToken) z = MBSFileAuthTokens() for token in tokens: toto = z.tokens.add() toto.FileID = h[token.FileID] toto.AuthToken = token.AuthToken return z def authorize_get(self, tokens, snapshot): if len(tokens.tokens) == 0: return self.headers2["x-apple-mmcs-auth"] = "%s %s" % ( tokens.tokens[0].FileID.encode("hex"), tokens.tokens[0].AuthToken) body = tokens.SerializeToString() file_groups = probobuf_request(self.content_host, "POST", URL[self.dsPrsID].authorizeGet(), body, self.headers2, FileGroups) file_chunks = {} pool = Pool(self.threads) containers = [] for group in file_groups.file_groups: for container_index, container in enumerate( group.storage_host_chunk_list): containers.append([group, container_index, container]) for res in pool.imap_unordered(self.download_chunks, containers): args, data = res group, container_index, container = args for file_ref in group.file_checksum_chunk_references: if file_ref.file_checksum not in self.files: continue decrypted_chunks = file_chunks.setdefault( file_ref.file_checksum, {}) for i, reference in enumerate(file_ref.chunk_references): if reference.container_index == container_index: decrypted_chunks[i] = data[reference.chunk_index] if len(decrypted_chunks) == len(file_ref.chunk_references): file = self.files[file_ref.file_checksum] try: self.write_file(file, decrypted_chunks, snapshot) except: raise else: # With iTunes style we need to keep the file if self.itunes_style: self.downloaded_files.append(file) del self.files[file_ref.file_checksum] pool.join() return file_groups def get_complete(self, mmcs_auth): self.headers2["x-apple-mmcs-auth"] = mmcs_auth body = "" probobuf_request(self.content_host, "POST", URL[self.dsPrsID].getComplete(), body, self.headers2) def download_chunks(self, args): group, container_index, container = args headers = {} # XXX for header in container.host_info.headers: headers[header.name] = header.value d = probobuf_request(container.host_info.hostname, container.host_info.method, container.host_info.uri, "", headers) decrypted = [] i = 0 for chunk in container.chunk_info: dchunk = decrypt_chunk(d[i:i + chunk.chunk_length], chunk.chunk_encryption_key, chunk.chunk_checksum) if dchunk: decrypted.append(dchunk) i += chunk.chunk_length return args, decrypted def write_file(self, file, decrypted_chunks, snapshot): # If the filename should be left in the iTunes backup style if self.itunes_style: if self.combined: directory = self.output_folder else: directory = os.path.join(self.output_folder, "snapshot_" + str(snapshot)) path_hash = hashlib.sha1( file.Domain.encode('utf-8') + "-" + file.RelativePath.encode('utf-8')).hexdigest() path = os.path.join(directory, path_hash) else: if self.combined: directory = os.path.join( self.output_folder, re.sub(r'[:|*<>?"]', "_", file.Domain)) path = os.path.join(directory, file.RelativePath) else: directory = os.path.join( self.output_folder, re.sub(r'[:|*<>?"]', "_", "snapshot_" + str(snapshot) + "/" + file.Domain)) path = os.path.join(directory, file.RelativePath) mkdir_p(os.path.dirname(path)) print '\t', file.Domain, '\t', path with open(path, "wb") as ff: hash = hashlib.sha1() for key, chunk in decrypted_chunks.iteritems(): hash.update(chunk) ff.write(chunk) # If file is encrypted if file.Attributes.EncryptionKey: key = file.Attributes.EncryptionKey ProtectionClass = struct.unpack(">L", key[0x18:0x1C])[0] if ProtectionClass == file.Attributes.ProtectionClass: wrapped_key = None filekey = None if file.Attributes.EncryptionKeyVersion and file.Attributes.EncryptionKeyVersion == 2: if self.kb.uuid == key[:0x10]: keyLength = struct.unpack(">L", key[0x20:0x24])[0] if keyLength == 0x48: wrapped_key = key[0x24:] else: wrapped_key = key[0x1C:] if wrapped_key: filekey = self.kb.unwrapCurve25519(ProtectionClass, wrapped_key) if not filekey: print "Failed to unwrap file key for file %s !!!" % file.RelativePath else: print "\tfilekey", filekey.encode("hex") self.decrypt_protected_file(path, filekey, file.Attributes.DecryptedSize) else: print "\tUnable to decrypt file, possible old backup format", file.RelativePath def decrypt_protected_file(self, path, filekey, decrypted_size=0): ivkey = hashlib.sha1(filekey).digest()[:16] hash = hashlib.sha1() sz = os.path.getsize(path) oldpath = path + ".encrypted" try: os.rename(path, oldpath) except: pass with open(oldpath, "rb") as old_file: with open(path, "wb") as new_file: n = sz / 0x1000 if decrypted_size: n += 1 for block in xrange(n): iv = AESencryptCBC(self.computeIV(block * 0x1000), ivkey) old_data = old_file.read(0x1000) hash.update(old_data) new_file.write(AESdecryptCBC(old_data, filekey, iv)) if decrypted_size == 0: #old iOS 5 format trailer = old_file.read(0x1C) decrypted_size = struct.unpack(">Q", trailer[:8])[0] assert hash.digest() == trailer[8:] new_file.truncate(decrypted_size) os.remove(oldpath) # Delete the encrypted file def computeIV(self, lba): iv = "" lba &= 0xffffffff for _ in xrange(4): if (lba & 1): lba = 0x80000061 ^ (lba >> 1) else: lba = lba >> 1 iv += struct.pack("<L", lba) return iv def download(self, backupUDID, item_types): mbsbackup = self.get_backup(backupUDID) self.output_folder = os.path.join(self.output_folder, backupUDID.encode("hex")) print "Downloading backup {} to {}".format(backupUDID.encode("hex"), self.output_folder) try: mkdir_p(self.output_folder) except OSError: print "Directory \"{}\" already exists.".format(self.output_folder) return keys = self.get_keys(backupUDID) if not keys or not len(keys.Key): print "get_keys FAILED!" return print "Got OTA Keybag" self.kb = Keybag(keys.Key[-1].KeyData) if not self.kb.unlockBackupKeybagWithPasscode(keys.Key[0].KeyData): print "Unable to unlock OTA keybag !" return print "Available Snapshots: %d" % (mbsbackup.Snapshot.SnapshotID) if self.chosen_snapshot_id == None: snapshot_list = [ 1, mbsbackup.Snapshot.SnapshotID - 1, mbsbackup.Snapshot.SnapshotID ] elif self.chosen_snapshot_id < 0: snapshot_list = [ mbsbackup.Snapshot.SnapshotID + self.chosen_snapshot_id + 1 ] # Remember chosen_snapshot_id is negative else: snapshot_list = [self.chosen_snapshot_id] for snapshot in snapshot_list: print "Listing snapshot %d..." % (snapshot) files = self.list_files(backupUDID, snapshot) print "Files in snapshot %d" % (len(files)) def matches_allowed_domain(a_file): return self.domain_filter in a_file.Domain def matches_allowed_item_types(a_file): return any(ITEM_TYPES_TO_FILE_NAMES[item_type] in a_file.RelativePath.lower() \ for item_type in item_types) if self.domain_filter: files = filter(matches_allowed_domain, files) if len(item_types) > 0: files = filter(matches_allowed_item_types, files) print "Downloading %d files due to filter" % (len(files)) if len(files): authTokens = self.get_files(backupUDID, snapshot, files) if len(authTokens.tokens) > 0: self.authorize_get(authTokens, snapshot) if self.itunes_style: self.write_info_plist(mbsbackup, snapshot) self.write_manifest_mbdb(snapshot) else: print "Unable to download snapshot. This snapshot may not have finished uploading yet." # Clean up self.files if not self.combined: self.downloaded_files = [] # Writes a plist file in the output_directory simular to that created by iTunes during backup def write_info_plist(self, mbsbackup, snapshot): if self.combined: directory = self.output_folder else: directory = os.path.join(self.output_folder, "snapshot_" + str(snapshot)) info_plist = { "Device Name": mbsbackup.Attributes.DeviceClass, "Display Name": mbsbackup.Attributes.DeviceClass, "Product Type": mbsbackup.Attributes.ProductType, "Serial Number": mbsbackup.Attributes.SerialNumber, "Target Type": "Device", "iTunes Version": "11.1", "Product Version": "8.1.1", # Must be higher than 4.0, current iTunes backup sets to 8.1.1 "Target Identifier": mbsbackup.backupUDID.encode("hex"), "Unique Identifier": mbsbackup.backupUDID.encode("hex") } with open(directory + "/Info.plist", 'wb') as fp: plistlib.writePlist(info_plist, fp) def write_manifest_mbdb(self, snapshot): if self.combined: directory = self.output_folder else: directory = os.path.join(self.output_folder, "snapshot_" + str(snapshot)) filename = os.path.join(directory, "Manifest.mbdb") # Generate the bare minimum MBDB file # Open file mbdb_file = open(filename, "wb") # Write file header mbdb_file.write("mbdb") mbdb_file.write("\x00\x00") # For each file for file in self.downloaded_files: # Write App Domain length mbdb_file.write(struct.pack('>h', len(file.Domain))) # Write App Domain mbdb_file.write(file.Domain) # Write iPhone Filename length mbdb_file.write(struct.pack('>h', len(file.RelativePath))) # Write iPhone Filename mbdb_file.write(file.RelativePath) # Write 0xFFFF for Link Target Length signifying that it is not present mbdb_file.write("\xFF\xFF") # Write 0xFFFF for SHAChecksum Length signifying the checksum is not present mbdb_file.write("\xFF\xFF") # Write 0xFFFF for the length of some unknown value, signifying it is not present mbdb_file.write("\xFF\xFF") # Write 0x27 bytes of 0x00, for the file properties as set by the iPhone during restore mbdb_file.write( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) # Write 0x00 for extended property count mbdb_file.write("\x00") # Close file mbdb_file.close()
def extract_backup(backup_path, output_path, password="", app=None): ''' if not os.path.exists(backup_path + "/Manifest.plist"): print "Manifest.plist not found" return manifest = readPlist(backup_path + "/Manifest.plist") ''' manifest = readManifest(backup_path) if manifest is None: print("Manifest.plist not found") return # dict = manifest['Applications'] # for apps in dict.iteritems(): # print "App Name: " + apps[0] # for key, value in apps[1].iteritems(): # print key + " : " + value # print "####################################" showinfo = readInfo(backup_path) if showinfo is None: print("Info.plist not found") return for i in showinfo: value = unicode(showinfo.get(i, "missing")) if i == "Product Type": value = value + " (" + getIDeviceProductName(value) + ")" print(i + " : " + value + "...") # print "Extract backup to %s ? (y/n)" % output_path # if raw_input() == "n": # return print("Backup is %sencrypted" % (int(not manifest["IsEncrypted"]) * "not ")) if manifest["IsEncrypted"] and password == "": print ("Enter backup password : "******"BackupKeyBag"): print ("No BackupKeyBag in manifest, assuming iOS 3.x backup") decrypt_backup3(backup_path, output_path, password) else: mbdb = MBDB(backup_path) kb = Keybag.createWithBackupManifest(manifest, password) if not kb: return manifest["password"] = password makedirs(output_path) plistlib.writePlist(manifest, output_path + "/Manifest.plist") mbdb.keybag = kb database, cursor = iOSBackupDB() store2db(cursor, mbdb) database.commit() print_domains(cursor) cursor.execute("Select * from indice where mbapp_name= ?", (app,)) records = cursor.fetchall() for record in records: dbrecord = MBFileRecordFromDB(record) mbdb.extract_backup_from_db(dbrecord, output_path)
def bf_system(): curdir = os.path.dirname(os.path.abspath(__file__)) client = RamdiskToolClient() di = client.getDeviceInfos() devicedir = di["udid"] if os.getcwd().find(devicedir) == -1: try: os.mkdir(devicedir) except: pass os.chdir(devicedir) key835 = di.get("key835").decode("hex") systembag = client.getSystemKeyBag() kbkeys = systembag["KeyBagKeys"].data kb = Keybag.createWithDataSignBlob(kbkeys, key835) keybags = di.setdefault("keybags", {}) kbuuid = kb.uuid.encode("hex") print "Keybag UUID :", kbuuid if True and keybags.has_key(kbuuid) and keybags[kbuuid].has_key("passcodeKey"): print "We've already seen this keybag" passcodeKey = keybags[kbuuid].get("passcodeKey").decode("hex") print kb.unlockWithPasscodeKey(passcodeKey) kb.printClassKeys() else: keybags[kbuuid] = {"KeyBagKeys": systembag["KeyBagKeys"]} di["KeyBagKeys"] = systembag["KeyBagKeys"] di.save() print "Enter passcode or leave blank for bruteforce:" z = raw_input() res = client.getPasscodeKey(systembag["KeyBagKeys"].data, z) if kb.unlockWithPasscodeKey(res.get("passcodeKey").decode("hex")): print 'Passcode "%s" OK' % z di.update(res) keybags[kbuuid].update(res) di.save() keychain_blob = client.downloadFile("/mnt2/Keychains/keychain-2.db") write_file("keychain-2.db", keychain_blob) # to fix the keychain downloading on iOS 7 keychain_shm = client.downloadFile("/mnt2/Keychains/keychain-2.db-shm") write_file("keychain-2.db-shm", keychain_shm) keychain_wal = client.downloadFile("/mnt2/Keychains/keychain-2.db-wal") write_file("keychain-2.db-wal", keychain_wal) print "Downloaded keychain database, use keychain_tool.py to decrypt secrets" return if z != "": print "Wrong passcode, trying to bruteforce !" if kb.passcodeComplexity == 0: print "Trying all 4-digits passcodes..." bf = client.bruteforceKeyBag(systembag["KeyBagKeys"].data) if bf: di.update(bf) keybags[kbuuid].update(bf) print bf print kb.unlockWithPasscodeKey(bf.get("passcodeKey").decode("hex")) kb.printClassKeys() di["classKeys"] = kb.getClearClassKeysDict() di.save() else: print "Complex passcode used, trying dictionary attack ..." dictfile = os.path.join(curdir, "wordlist.dict") try: wordlist = open(dictfile, "r").readlines() except (OSError, IOError), e: exit(e) for line in wordlist: res = client.getPasscodeKey(systembag["KeyBagKeys"].data, line.rstrip("\n")) if kb.unlockWithPasscodeKey(res.get("passcodeKey").decode("hex")): print 'Passcode "%s" OK' % line.rstrip("\n") di.update(res) keybags[kbuuid].update(res) di.save() keychain_blob = client.downloadFile("/mnt2/Keychains/keychain-2.db") write_file("keychain-2.db", keychain_blob) # to fix the keychain downloading on iOS 7 keychain_shm = client.downloadFile("/mnt2/Keychains/keychain-2.db-shm") write_file("keychain-2.db-shm", keychain_shm) keychain_wal = client.downloadFile("/mnt2/Keychains/keychain-2.db-wal") write_file("keychain-2.db-wal", keychain_wal) print "Downloaded keychain database, use keychain_tool.py to decrypt secrets" return print "Passcode not found!" return
def main(): parser = OptionParser( usage= "%prog keychain.db/keychain-backup.plist keyfile.plist/Manifest.plist") parser.add_option("-d", "--display", dest="display", action="store_true", default=False, help="Show keychain items on stdout") parser.add_option("-s", "--sanitize", dest="sanitize", action="store_true", default=False, help="Hide secrets on stdout with ***") parser.add_option("-p", "--passwords", dest="passwords", action="store_true", default=False, help="Save generic & internet passwords as CSV file") parser.add_option("-c", "--certs", dest="certs", action="store_true", default=False, help="Extract certificates and keys") parser.add_option("-o", "--old", dest="oldpass", action="store_true", default=False, help="Bruteforce old passcodes") (options, args) = parser.parse_args() if len(args) < 2: parser.print_help() return p = readPlist(args[1]) if p.has_key("BackupKeyBag"): deviceKey = None if p.has_key("key835"): deviceKey = p["key835"].decode("hex") else: if not p["IsEncrypted"]: print "This backup is not encrypted, without key 835 nothing in the keychain can be decrypted" print "If you have key835 for device %s enter it (in hex)" % p[ "Lockdown"]["UniqueDeviceID"] d = raw_input() if len(d) == 32: p["key835"] = d deviceKey = d.decode("hex") plistlib.writePlist(p, args[1]) kb = Keybag.createWithBackupManifest(p, p.get("password", ""), deviceKey) if not kb: return k = Keychain4(args[0], kb) else: kb = Keybag.createWithPlist(p) k = keychain_load(args[0], kb, p["key835"].decode("hex")) if options.display: k.print_all(options.sanitize) if options.passwords: k.save_passwords() if options.certs: k.save_certs_keys() if options.oldpass: mc = k.get_managed_configuration() if not mc: print "Managed configuration not found" return print "Bruteforcing %d old passcodes" % len(mc.get("history", [])) for h in mc["history"]: p = bruteforce_old_pass(h) if p: print "Found : %s" % p else: print "Not Found"