Example #1
0
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
Example #2
0
    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)
Example #3
0
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
Example #4
0
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
Example #5
0
    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)
Example #6
0
    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
Example #7
0
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)
Example #8
0
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()
Example #9
0
    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)
Example #10
0
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"
Example #12
0
    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)
Example #13
0
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
Example #14
0
File: iloot.py Project: yike8/iloot
    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"
Example #16
0
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")
Example #17
0
    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)
Example #18
0
	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"
Example #20
0
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?"
Example #23
0
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)
Example #24
0
File: iloot.py Project: yike8/iloot
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
Example #26
0
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"
Example #28
0
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
Example #29
0
    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 = []
Example #30
0
    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 = []
Example #31
0
    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
Example #32
0
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")
Example #33
0
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"
Example #35
0
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()
Example #36
0
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"