¿Cómo descifrar una copia de seguridad de iPhone Apple iTunes cifrada?

84

Varios usuarios desafortunados de iPhone me han pedido que los ayude a restaurar los datos de sus copias de seguridad de iTunes. Esto es fácil cuando no están encriptados, pero no cuando están encriptados, se conozca o no la contraseña.

Como tal, estoy tratando de averiguar el esquema de cifrado que se usa en los archivos mddata y mdinfo cuando se cifran. De lo contrario, no tengo problemas para leer estos archivos y he creado algunas bibliotecas C # robustas para hacerlo. (Si puedes ayudar, no me importa qué idioma uses. ¡Es el principio que busco aquí!)

La "Guía de implementación empresarial de iPhone OS" de Apple establece que "Las copias de seguridad del dispositivo se pueden almacenar en formato cifrado seleccionando la opción Cifrar copia de seguridad del iPhone en el panel de resumen del dispositivo de iTunes. Los archivos se cifran mediante AES128 con una clave de 256 bits. La clave es almacenado de forma segura en el llavero del iPhone ".

Esa es una pista bastante buena, y hay buena información aquí sobre Stackoverflow en la interoperabilidad AES / Rijndael de iPhone, lo que sugiere que se puede usar un tamaño de clave de 128 y el modo CBC.

Aparte de cualquier otra ofuscación, se requieren una clave y un vector de inicialización (IV) / sal.

Se podría suponer que la clave es una manipulación de la "contraseña de respaldo" que iTunes solicita a los usuarios que ingresen y que se pasa a " AppleMobileBackup.exe ", rellenado de la manera dictada por CBC. Sin embargo, dada la referencia al llavero del iPhone, me pregunto si la "contraseña de respaldo" podría no usarse como contraseña en un certificado X509 o clave privada simétrica, y si el certificado o la clave privada en sí podrían usarse como clave. ( AES y el proceso de cifrado / descifrado de iTunes es simétrico).

La intravenosa es otro asunto, y podrían ser algunas cosas. Tal vez sea una de las claves codificadas en iTunes o en los propios dispositivos .

Aunque el comentario de Apple anterior sugiere que la clave está presente en el llavero del dispositivo, creo que esto no es tan importante. Se puede restaurar una copia de seguridad cifrada en un dispositivo diferente , lo que sugiere que toda la información relevante para el descifrado está presente en la copia de seguridad y la configuración de iTunes, y que cualquier cosa que esté únicamente en el dispositivo es irrelevante y reemplazable en este contexto. Entonces, ¿dónde podría estar la clave?

He enumerado las rutas a continuación desde una máquina con Windows, pero es mucho más grande, independientemente del sistema operativo que usemos.

"\ Appdata \ Roaming \ Apple Computer \ iTunes \ itunesprefs.xml" contiene una PList con una entrada de dictado "Keychain". El "\ programdata \ apple \ Lockdown \ 09037027da8f4bdefdea97d706703ca034c88bab.plist" contiene un PList con "DeviceCertificate", "HostCertificate" y "RootCertificate", todos los cuales parecen ser certificados X509 válidos. El mismo archivo también parece contener claves asimétricas "RootPrivateKey" y "HostPrivateKey" (mi lectura sugiere que podrían estar envueltas en PKCS # 7). Además, dentro de cada copia de seguridad hay valores "AuthSignature" y "AuthData" en el archivo Manifest.plist, aunque estos parecen rotarse a medida que se realiza una copia de seguridad incremental de cada archivo, sugirió que no son tan útiles como clave, a menos que algo realmente se está haciendo bastante complicado.

Hay muchas cosas engañosas que sugieren que obtener datos de copias de seguridad cifradas es fácil. No lo es, y que yo sepa, no se ha hecho. Omitir o deshabilitar el cifrado de respaldo es otro asunto completamente diferente, y no es lo que estoy buscando hacer.

No se trata de piratear el iPhone ni nada de eso. Todo lo que busco aquí es un medio para extraer datos (fotos, contactos, etc.) de las copias de seguridad de iTunes cifradas, como puedo hacerlo con las no cifradas. Probé todo tipo de permutaciones con la información que escribí arriba pero no llegué a ninguna parte. Agradecería cualquier pensamiento o técnica que pudiera haber pasado por alto.

Aidan Fitzpatrick
fuente
3
Actualización a partir de tres años: lo descubrí y lo convertí en un producto que está disponible gratuitamente. Estaba en el camino correcto arriba, pero era difícil.
Aidan Fitzpatrick
¿Tiene un enlace para ese producto?
Thilo
1
Como preguntaste, es el iPhone Backup Extractor . Si bien verá que hay ediciones pagas del programa, la antigua edición gratuita simple le permitirá obtener archivos cifrados 4 a la vez.
Aidan Fitzpatrick
1
Veo que lograste descifrar la copia de seguridad 10.2. ¿Puedes compartir cómo lo lograste?
Niki
@Niki He actualizado mi respuesta a continuación para iOS 10
andrewdotn

Respuestas:

105

Los investigadores de seguridad Jean-Baptiste Bédrune y Jean Sigwald presentaron cómo hacer esto en Hack-in-the-box Amsterdam 2011 .

Desde entonces, Apple ha publicado un documento técnico de seguridad de iOS con más detalles sobre claves y algoritmos, y Charlie Miller et al. han lanzado el iOS Hacker's Handbook , que cubre algunos de los mismos temas de una manera práctica. Cuando apareció iOS 10 por primera vez, hubo cambios en el formato de copia de seguridad que Apple no publicó al principio, pero varias personas realizaron ingeniería inversa en los cambios de formato .

Las copias de seguridad cifradas son excelentes

Lo mejor de las copias de seguridad de iPhone cifradas es que contienen cosas como contraseñas WiFi que no se encuentran en copias de seguridad no cifradas habituales. Como se discutió en el Informe de seguridad de iOS , las copias de seguridad cifradas se consideran más "seguras", por lo que Apple considera que está bien incluir información más confidencial en ellas.

Una advertencia importante: obviamente, descifrar la copia de seguridad de su dispositivo iOS elimina su cifrado. Para proteger su privacidad y seguridad, solo debe ejecutar estos scripts en una máquina con cifrado de disco completo. Si bien es posible que un experto en seguridad escriba software que proteja las claves en la memoria, por ejemplo, mediante el uso de funciones como VirtualLock()y SecureZeroMemory()entre muchas otras cosas, estos scripts de Python almacenarán sus claves de cifrado y contraseñas en cadenas para que Python las recoja como basura. Esto significa que sus claves secretas y contraseñas vivirán en la RAM por un tiempo, desde donde se filtrarán en su archivo de intercambio y en su disco, donde un adversario puede recuperarlas. Esto anula por completo el punto de tener una copia de seguridad cifrada.

Cómo descifrar copias de seguridad: en teoría

El Whitepaper de seguridad de iOS explica los conceptos fundamentales de claves por archivo, clases de protección, claves de clase de protección y bolsas de claves mejor que yo. Si aún no está familiarizado con estos, tómese unos minutos para leer las partes relevantes.

Ahora sabe que cada archivo en iOS está cifrado con su propia clave de cifrado aleatoria por archivo, pertenece a una clase de protección y las claves de cifrado por archivo se almacenan en los metadatos del sistema de archivos, envueltas en la clave de clase de protección.

Para descifrar:

  1. Decodifica la bolsa de llaves almacenada en la BackupKeyBagentrada de Manifest.plist. En el documento técnico se ofrece una descripción general de alto nivel de esta estructura . La Wiki del iPhone describe el formato binario: un campo de tipo cadena de 4 bytes, un campo de longitud big-endian de 4 bytes y luego el valor en sí.

    Los valores importantes son las aciones ITERPBKDF2 y SALTla sal de doble protección DPSLy el recuento de iteraciones DPIC, y luego para cada protección CLS, la WPKYclave envuelta.

  2. Usando la contraseña de respaldo, obtenga una clave de 32 bytes usando la sal de PBKDF2 correcta y el número de iteraciones. Primero use una ronda SHA256 con DPSLy DPIC, luego una ronda SHA1 con ITERy SALT.

    Desenvuelva cada llave envuelta de acuerdo con RFC 3394 .

  3. Descifrar la base de datos manifiesta tirando de la clase de protección de 4 bytes y la clave más larga de la ManifestKeyde Manifest.plisty desenvolverlo. Ahora tiene una base de datos SQLite con todos los metadatos del archivo.

  4. Para cada archivo de interés, obtenga la clave de cifrado por archivo cifrada por clase y el código de clase de protección buscando en la Files.filecolumna de la base de datos un plist binario que contenga EncryptionKeyy ProtectionClassentradas. Quite la etiqueta de longitud inicial de cuatro bytes EncryptionKeyantes de usarla.

    Luego, obtenga la clave de descifrado final desenvolviéndola con la clave de clase que se desenvolvió con la contraseña de respaldo. Luego, descifre el archivo usando AES en modo CBC con un IV cero.

Cómo descifrar copias de seguridad: en la práctica

Primero necesitará algunas dependencias de biblioteca. Si está en una Mac usando un Python 2.7 o 3.7 instalado en Homebrew, puede instalar las dependencias con:

CFLAGS="-I$(brew --prefix)/opt/openssl/include" \
LDFLAGS="-L$(brew --prefix)/opt/openssl/lib" \    
    pip install biplist fastpbkdf2 pycrypto

En forma de código fuente ejecutable, aquí se explica cómo descifrar un solo archivo de preferencias de una copia de seguridad de iPhone cifrada:

#!/usr/bin/env python3.7
# coding: UTF-8

from __future__ import print_function
from __future__ import division

import argparse
import getpass
import os.path
import pprint
import random
import shutil
import sqlite3
import string
import struct
import tempfile
from binascii import hexlify

import Crypto.Cipher.AES # https://www.dlitz.net/software/pycrypto/
import biplist
import fastpbkdf2
from biplist import InvalidPlistException


def main():
    ## Parse options
    parser = argparse.ArgumentParser()
    parser.add_argument('--backup-directory', dest='backup_directory',
                    default='testdata/encrypted')
    parser.add_argument('--password-pipe', dest='password_pipe',
                        help="""\
Keeps password from being visible in system process list.
Typical use: --password-pipe=<(echo -n foo)
""")
    parser.add_argument('--no-anonymize-output', dest='anonymize',
                        action='store_false')
    args = parser.parse_args()
    global ANONYMIZE_OUTPUT
    ANONYMIZE_OUTPUT = args.anonymize
    if ANONYMIZE_OUTPUT:
        print('Warning: All output keys are FAKE to protect your privacy')

    manifest_file = os.path.join(args.backup_directory, 'Manifest.plist')
    with open(manifest_file, 'rb') as infile:
        manifest_plist = biplist.readPlist(infile)
    keybag = Keybag(manifest_plist['BackupKeyBag'])
    # the actual keys are unknown, but the wrapped keys are known
    keybag.printClassKeys()

    if args.password_pipe:
        password = readpipe(args.password_pipe)
        if password.endswith(b'\n'):
            password = password[:-1]
    else:
        password = getpass.getpass('Backup password: ').encode('utf-8')

    ## Unlock keybag with password
    if not keybag.unlockWithPasscode(password):
        raise Exception('Could not unlock keybag; bad password?')
    # now the keys are known too
    keybag.printClassKeys()

    ## Decrypt metadata DB
    manifest_key = manifest_plist['ManifestKey'][4:]
    with open(os.path.join(args.backup_directory, 'Manifest.db'), 'rb') as db:
        encrypted_db = db.read()

    manifest_class = struct.unpack('<l', manifest_plist['ManifestKey'][:4])[0]
    key = keybag.unwrapKeyForClass(manifest_class, manifest_key)
    decrypted_data = AESdecryptCBC(encrypted_db, key)

    temp_dir = tempfile.mkdtemp()
    try:
        # Does anyone know how to get Python’s SQLite module to open some
        # bytes in memory as a database?
        db_filename = os.path.join(temp_dir, 'db.sqlite3')
        with open(db_filename, 'wb') as db_file:
            db_file.write(decrypted_data)
        conn = sqlite3.connect(db_filename)
        conn.row_factory = sqlite3.Row
        c = conn.cursor()
        # c.execute("select * from Files limit 1");
        # r = c.fetchone()
        c.execute("""
            SELECT fileID, domain, relativePath, file
            FROM Files
            WHERE relativePath LIKE 'Media/PhotoData/MISC/DCIM_APPLE.plist'
            ORDER BY domain, relativePath""")
        results = c.fetchall()
    finally:
        shutil.rmtree(temp_dir)

    for item in results:
        fileID, domain, relativePath, file_bplist = item

        plist = biplist.readPlistFromString(file_bplist)
        file_data = plist['$objects'][plist['$top']['root'].integer]
        size = file_data['Size']

        protection_class = file_data['ProtectionClass']
        encryption_key = plist['$objects'][
            file_data['EncryptionKey'].integer]['NS.data'][4:]

        backup_filename = os.path.join(args.backup_directory,
                                    fileID[:2], fileID)
        with open(backup_filename, 'rb') as infile:
            data = infile.read()
            key = keybag.unwrapKeyForClass(protection_class, encryption_key)
            # truncate to actual length, as encryption may introduce padding
            decrypted_data = AESdecryptCBC(data, key)[:size]

        print('== decrypted data:')
        print(wrap(decrypted_data))
        print()

        print('== pretty-printed plist')
        pprint.pprint(biplist.readPlistFromString(decrypted_data))

##
# this section is mostly copied from parts of iphone-dataprotection
# http://code.google.com/p/iphone-dataprotection/

CLASSKEY_TAGS = [b"CLAS",b"WRAP",b"WPKY", b"KTYP", b"PBKY"]  #UUID
KEYBAG_TYPES = ["System", "Backup", "Escrow", "OTA (icloud)"]
KEY_TYPES = ["AES", "Curve25519"]
PROTECTION_CLASSES={
    1:"NSFileProtectionComplete",
    2:"NSFileProtectionCompleteUnlessOpen",
    3:"NSFileProtectionCompleteUntilFirstUserAuthentication",
    4:"NSFileProtectionNone",
    5:"NSFileProtectionRecovery?",

    6: "kSecAttrAccessibleWhenUnlocked",
    7: "kSecAttrAccessibleAfterFirstUnlock",
    8: "kSecAttrAccessibleAlways",
    9: "kSecAttrAccessibleWhenUnlockedThisDeviceOnly",
    10: "kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly",
    11: "kSecAttrAccessibleAlwaysThisDeviceOnly"
}
WRAP_DEVICE = 1
WRAP_PASSCODE = 2

class Keybag(object):
    def __init__(self, data):
        self.type = None
        self.uuid = None
        self.wrap = None
        self.deviceKey = None
        self.attrs = {}
        self.classKeys = {}
        self.KeyBagKeys = None #DATASIGN blob
        self.parseBinaryBlob(data)

    def parseBinaryBlob(self, data):
        currentClassKey = None

        for tag, data in loopTLVBlocks(data):
            if len(data) == 4:
                data = struct.unpack(">L", data)[0]
            if tag == b"TYPE":
                self.type = data
                if self.type > 3:
                    print("FAIL: keybag type > 3 : %d" % self.type)
            elif tag == b"UUID" and self.uuid is None:
                self.uuid = data
            elif tag == b"WRAP" and self.wrap is None:
                self.wrap = data
            elif tag == b"UUID":
                if currentClassKey:
                    self.classKeys[currentClassKey[b"CLAS"]] = currentClassKey
                currentClassKey = {b"UUID": data}
            elif tag in CLASSKEY_TAGS:
                currentClassKey[tag] = data
            else:
                self.attrs[tag] = data
        if currentClassKey:
            self.classKeys[currentClassKey[b"CLAS"]] = currentClassKey

    def unlockWithPasscode(self, passcode):
        passcode1 = fastpbkdf2.pbkdf2_hmac('sha256', passcode,
                                        self.attrs[b"DPSL"],
                                        self.attrs[b"DPIC"], 32)
        passcode_key = fastpbkdf2.pbkdf2_hmac('sha1', passcode1,
                                            self.attrs[b"SALT"],
                                            self.attrs[b"ITER"], 32)
        print('== Passcode key')
        print(anonymize(hexlify(passcode_key)))
        for classkey in self.classKeys.values():
            if b"WPKY" not in classkey:
                continue
            k = classkey[b"WPKY"]
            if classkey[b"WRAP"] & WRAP_PASSCODE:
                k = AESUnwrap(passcode_key, classkey[b"WPKY"])
                if not k:
                    return False
                classkey[b"KEY"] = k
        return True

    def unwrapKeyForClass(self, protection_class, persistent_key):
        ck = self.classKeys[protection_class][b"KEY"]
        if len(persistent_key) != 0x28:
            raise Exception("Invalid key length")
        return AESUnwrap(ck, persistent_key)

    def printClassKeys(self):
        print("== Keybag")
        print("Keybag type: %s keybag (%d)" % (KEYBAG_TYPES[self.type], self.type))
        print("Keybag version: %d" % self.attrs[b"VERS"])
        print("Keybag UUID: %s" % anonymize(hexlify(self.uuid)))
        print("-"*209)
        print("".join(["Class".ljust(53),
                    "WRAP".ljust(5),
                    "Type".ljust(11),
                    "Key".ljust(65),
                    "WPKY".ljust(65),
                    "Public key"]))
        print("-"*208)
        for k, ck in self.classKeys.items():
            if k == 6:print("")

            print("".join(
                [PROTECTION_CLASSES.get(k).ljust(53),
                str(ck.get(b"WRAP","")).ljust(5),
                KEY_TYPES[ck.get(b"KTYP",0)].ljust(11),
                anonymize(hexlify(ck.get(b"KEY", b""))).ljust(65),
                anonymize(hexlify(ck.get(b"WPKY", b""))).ljust(65),
            ]))
        print()

def loopTLVBlocks(blob):
    i = 0
    while i + 8 <= len(blob):
        tag = blob[i:i+4]
        length = struct.unpack(">L",blob[i+4:i+8])[0]
        data = blob[i+8:i+8+length]
        yield (tag,data)
        i += 8 + length

def unpack64bit(s):
    return struct.unpack(">Q",s)[0]
def pack64bit(s):
    return struct.pack(">Q",s)

def AESUnwrap(kek, wrapped):
    C = []
    for i in range(len(wrapped)//8):
        C.append(unpack64bit(wrapped[i*8:i*8+8]))
    n = len(C) - 1
    R = [0] * (n+1)
    A = C[0]

    for i in range(1,n+1):
        R[i] = C[i]

    for j in reversed(range(0,6)):
        for i in reversed(range(1,n+1)):
            todec = pack64bit(A ^ (n*j+i))
            todec += pack64bit(R[i])
            B = Crypto.Cipher.AES.new(kek).decrypt(todec)
            A = unpack64bit(B[:8])
            R[i] = unpack64bit(B[8:])

    if A != 0xa6a6a6a6a6a6a6a6:
        return None
    res = b"".join(map(pack64bit, R[1:]))
    return res

ZEROIV = "\x00"*16
def AESdecryptCBC(data, key, iv=ZEROIV, padding=False):
    if len(data) % 16:
        print("AESdecryptCBC: data length not /16, truncating")
        data = data[0:(len(data)/16) * 16]
    data = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CBC, iv).decrypt(data)
    if padding:
        return removePadding(16, data)
    return data

##
# here are some utility functions, one making sure I don’t leak my
# secret keys when posting the output on Stack Exchange

anon_random = random.Random(0)
memo = {}
def anonymize(s):
    if type(s) == str:
        s = s.encode('utf-8')
    global anon_random, memo
    if ANONYMIZE_OUTPUT:
        if s in memo:
            return memo[s]
        possible_alphabets = [
            string.digits,
            string.digits + 'abcdef',
            string.ascii_letters,
            "".join(chr(x) for x in range(0, 256)),
        ]
        for a in possible_alphabets:
            if all((chr(c) if type(c) == int else c) in a for c in s):
                alphabet = a
                break
        ret = "".join([anon_random.choice(alphabet) for i in range(len(s))])
        memo[s] = ret
        return ret
    else:
        return s

def wrap(s, width=78):
    "Return a width-wrapped repr(s)-like string without breaking on \’s"
    s = repr(s)
    quote = s[0]
    s = s[1:-1]
    ret = []
    while len(s):
        i = s.rfind('\\', 0, width)
        if i <= width - 4: # "\x??" is four characters
            i = width
        ret.append(s[:i])
        s = s[i:]
    return '\n'.join("%s%s%s" % (quote, line ,quote) for line in ret)

def readpipe(path):
    if stat.S_ISFIFO(os.stat(path).st_mode):
        with open(path, 'rb') as pipe:
            return pipe.read()
    else:
        raise Exception("Not a pipe: {!r}".format(path))

if __name__ == '__main__':
    main()

Que luego imprime esta salida:

Warning: All output keys are FAKE to protect your privacy
== Keybag
Keybag type: Backup keybag (1)
Keybag version: 3
Keybag UUID: dc6486c479e84c94efce4bea7169ef7d
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Class                                                WRAP Type       Key                                                              WPKY                                                             Public key
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
NSFileProtectionComplete                             2    AES                                                                         4c80b6da07d35d393fc7158e18b8d8f9979694329a71ceedee86b4cde9f97afec197ad3b13c5d12b
NSFileProtectionCompleteUnlessOpen                   2    AES                                                                         09e8a0a9965f00f213ce06143a52801f35bde2af0ad54972769845d480b5043f545fa9b66a0353a6
NSFileProtectionCompleteUntilFirstUserAuthentication 2    AES                                                                         e966b6a0742878ce747cec3fa1bf6a53b0d811ad4f1d6147cd28a5d400a8ffe0bbabea5839025cb5
NSFileProtectionNone                                 2    AES                                                                         902f46847302816561e7df57b64beea6fa11b0068779a65f4c651dbe7a1630f323682ff26ae7e577
NSFileProtectionRecovery?                            3    AES                                                                         a3935fed024cd9bc11d0300d522af8e89accfbe389d7c69dca02841df46c0a24d0067dba2f696072

kSecAttrAccessibleWhenUnlocked                       2    AES                                                                         09a1856c7e97a51a9c2ecedac8c3c7c7c10e7efa931decb64169ee61cb07a0efb115050fd1e33af1
kSecAttrAccessibleAfterFirstUnlock                   2    AES                                                                         0509d215f2f574efa2f192efc53c460201168b26a175f066b5347fc48bc76c637e27a730b904ca82
kSecAttrAccessibleAlways                             2    AES                                                                         b7ac3c4f1e04896144ce90c4583e26489a86a6cc45a2b692a5767b5a04b0907e081daba009fdbb3c
kSecAttrAccessibleWhenUnlockedThisDeviceOnly         3    AES                                                                         417526e67b82e7c6c633f9063120a299b84e57a8ffee97b34020a2caf6e751ec5750053833ab4d45
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly     3    AES                                                                         b0e17b0cf7111c6e716cd0272de5684834798431c1b34bab8d1a1b5aba3d38a3a42c859026f81ccc
kSecAttrAccessibleAlwaysThisDeviceOnly               3    AES                                                                         9b3bdc59ae1d85703aa7f75d49bdc600bf57ba4a458b20a003a10f6e36525fb6648ba70e6602d8b2

== Passcode key
ee34f5bb635830d698074b1e3e268059c590973b0f1138f1954a2a4e1069e612

== Keybag
Keybag type: Backup keybag (1)
Keybag version: 3
Keybag UUID: dc6486c479e84c94efce4bea7169ef7d
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Class                                                WRAP Type       Key                                                              WPKY                                                             Public key
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
NSFileProtectionComplete                             2    AES        64e8fc94a7b670b0a9c4a385ff395fe9ba5ee5b0d9f5a5c9f0202ef7fdcb386f 4c80b6da07d35d393fc7158e18b8d8f9979694329a71ceedee86b4cde9f97afec197ad3b13c5d12b
NSFileProtectionCompleteUnlessOpen                   2    AES        22a218c9c446fbf88f3ccdc2ae95f869c308faaa7b3e4fe17b78cbf2eeaf4ec9 09e8a0a9965f00f213ce06143a52801f35bde2af0ad54972769845d480b5043f545fa9b66a0353a6
NSFileProtectionCompleteUntilFirstUserAuthentication 2    AES        1004c6ca6e07d2b507809503180edf5efc4a9640227ac0d08baf5918d34b44ef e966b6a0742878ce747cec3fa1bf6a53b0d811ad4f1d6147cd28a5d400a8ffe0bbabea5839025cb5
NSFileProtectionNone                                 2    AES        2e809a0cd1a73725a788d5d1657d8fd150b0e360460cb5d105eca9c60c365152 902f46847302816561e7df57b64beea6fa11b0068779a65f4c651dbe7a1630f323682ff26ae7e577
NSFileProtectionRecovery?                            3    AES        9a078d710dcd4a1d5f70ea4062822ea3e9f7ea034233e7e290e06cf0d80c19ca a3935fed024cd9bc11d0300d522af8e89accfbe389d7c69dca02841df46c0a24d0067dba2f696072

kSecAttrAccessibleWhenUnlocked                       2    AES        606e5328816af66736a69dfe5097305cf1e0b06d6eb92569f48e5acac3f294a4 09a1856c7e97a51a9c2ecedac8c3c7c7c10e7efa931decb64169ee61cb07a0efb115050fd1e33af1
kSecAttrAccessibleAfterFirstUnlock                   2    AES        6a4b5292661bac882338d5ebb51fd6de585befb4ef5f8ffda209be8ba3af1b96 0509d215f2f574efa2f192efc53c460201168b26a175f066b5347fc48bc76c637e27a730b904ca82
kSecAttrAccessibleAlways                             2    AES        c0ed717947ce8d1de2dde893b6026e9ee1958771d7a7282dd2116f84312c2dd2 b7ac3c4f1e04896144ce90c4583e26489a86a6cc45a2b692a5767b5a04b0907e081daba009fdbb3c
kSecAttrAccessibleWhenUnlockedThisDeviceOnly         3    AES        80d8c7be8d5103d437f8519356c3eb7e562c687a5e656cfd747532f71668ff99 417526e67b82e7c6c633f9063120a299b84e57a8ffee97b34020a2caf6e751ec5750053833ab4d45
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly     3    AES        a875a15e3ff901351c5306019e3b30ed123e6c66c949bdaa91fb4b9a69a3811e b0e17b0cf7111c6e716cd0272de5684834798431c1b34bab8d1a1b5aba3d38a3a42c859026f81ccc
kSecAttrAccessibleAlwaysThisDeviceOnly               3    AES        1e7756695d337e0b06c764734a9ef8148af20dcc7a636ccfea8b2eb96a9e9373 9b3bdc59ae1d85703aa7f75d49bdc600bf57ba4a458b20a003a10f6e36525fb6648ba70e6602d8b2

== decrypted data:
'<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD '
'PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist versi'
'on="1.0">\n<dict>\n\t<key>DCIMLastDirectoryNumber</key>\n\t<integer>100</integ'
'er>\n\t<key>DCIMLastFileNumber</key>\n\t<integer>3</integer>\n</dict>\n</plist'
'>\n'

== pretty-printed plist
{'DCIMLastDirectoryNumber': 100, 'DCIMLastFileNumber': 3}

Crédito adicional

El código de protección de datos de iPhone publicado por Bédrune y Sigwald puede descifrar el llavero de una copia de seguridad, incluidas cosas divertidas como wifi guardado y contraseñas de sitios web:

$ python iphone-dataprotection/python_scripts/keychain_tool.py ...

--------------------------------------------------------------------------------------
|                              Passwords                                             |
--------------------------------------------------------------------------------------
|Service           |Account          |Data           |Access group  |Protection class|
--------------------------------------------------------------------------------------
|AirPort           |Ed’s Coffee Shop |<3FrenchRoast  |apple         |AfterFirstUnlock|
...

Ese código ya no funciona en las copias de seguridad de los teléfonos que utilizan el último iOS, pero hay algunos puertos golang que se han mantenido actualizados para permitir el acceso al llavero .

andrewdotn
fuente
2
Lo probé y funcionó donde todas las demás herramientas fallaron. Tuve que agregar también otro módulo de Python faltante: pip install --user pycrypto ¡Gracias!
ALoopingIcon
2
¡Maravilloso! Usando macports instalé estas dependencias: py27-m2crypto py27-cryptography
hiperespasmo
2
El enlace de code.google.com era 404 para mí, pero encontré una versión actualizada de la herramienta (para OS X 10.10) en github.com/dinosec/iphone-dataprotection . Funcionó en OS X 10.11.5.
Aaron Brager
2
¡Si actualizaras el código, estarías cerca de una deidad para mí!
Jonas Zaugg
3
@JonasZaugg He actualizado el código de muestra para iOS 10. Espero que lo encuentre útil.
andrewdotn
6

Lo siento, pero incluso podría ser más complicado, involucrando pbkdf2, o incluso una variación de él. Escuche la sesión # 209 de la WWDC 2010, que habla principalmente sobre las medidas de seguridad en iOS 4, pero también menciona brevemente el cifrado por separado de las copias de seguridad y cómo están relacionadas.

Puede estar bastante seguro de que sin conocer la contraseña, no hay forma de que pueda descifrarla, ni siquiera mediante la fuerza bruta.

Supongamos que desea intentar permitir que las personas que CONOCEN la contraseña accedan a los datos de sus copias de seguridad.

Me temo que no hay forma de evitar mirar el código real en iTunes para averiguar qué algoritmos se emplean.

En los días de Newton, tenía que descifrar datos de un programa y podía llamar a su función de descifrado directamente (conociendo la contraseña, por supuesto) sin la necesidad de siquiera entender su algoritmo. Desafortunadamente, ya no es tan fácil.

Estoy seguro de que hay personas capacitadas que podrían aplicar ingeniería inversa al código de iTunes; solo tienes que interesarlos.

En teoría, los algoritmos de Apple deberían diseñarse de manera que los datos sigan siendo seguros (es decir, prácticamente irrompibles mediante métodos de fuerza bruta) para cualquier atacante que conozca el método de cifrado exacto. Y en la sesión 209 de la WWDC profundizaron bastante en los detalles sobre lo que hacen para lograrlo. Tal vez puedas obtener respuestas directamente del equipo de seguridad de Apple si les dices tus buenas intenciones. Después de todo, incluso ellos deberían saber que la seguridad por ofuscación no es realmente eficiente. Prueba su lista de correo de seguridad. Incluso si no responde, tal vez alguien más silenciosamente en la lista responda con algo de ayuda.

¡Buena suerte!

Thomas Tempelmann
fuente
1

No lo he probado, pero Elcomsoft lanzó un producto que, según ellos, es capaz de descifrar copias de seguridad, con fines forenses. Quizás no sea tan genial como diseñar una solución usted mismo, pero podría ser más rápido.

http://www.elcomsoft.com/eppb.html

Jablair
fuente
1
Gracias. Esto no descifra las copias de seguridad como tales: simplemente descifra las claves. Ya sé cómo hacer eso ... y eso es mucho más simple que descifrar la copia de seguridad una vez que tienes la clave.
Aidan Fitzpatrick
-3

Debería obtener una copia de la utilidad de línea de comandos mdhelper de Erica Sadun ( código binario y fuente de OS X ). Admite enumerar y extraer el contenido de las copias de seguridad de iPhone / iPod Touch, incluida la libreta de direcciones y bases de datos de SMS, y otros metadatos y configuraciones de aplicaciones.

Nathan de Vries
fuente
1
Eso es solo un lector PList: ya puedo hacer eso de forma nativa. No admite copias de seguridad cifradas, que es lo que busco y que está más allá del alcance de esa utilidad.
Aidan Fitzpatrick
¿Te tomaste el tiempo para probar la utilidad? Mis copias de seguridad están encriptadas y hacen exactamente lo que estás intentando hacer.
Nathan de Vries
10
Sí, y también he leído la fuente. No maneja copias de seguridad encriptadas y se modificó por última vez antes del lanzamiento del soporte de iTunes para copias de seguridad encriptadas. Sospecho que te refieres a que tus copias de seguridad están codificadas o que tu iPhone usa un sistema de archivos encriptado, que es otra cuestión. Además de que no hay soporte para el cifrado en el código, es más obvio que no hay opción para pasar una contraseña en las opciones de la línea de comandos. Y el código no usa certificados ni llaveros. Me encantaría que me demuestren que estoy equivocado en esto, ¡pero realmente no creo que lo esté! Sin embargo, agradezco la sugerencia.
Aidan Fitzpatrick