多くの不幸なiPhoneユーザーから、iTunesバックアップからデータを復元するのを手伝ってほしいと頼まれました。暗号化されていない場合は簡単ですが、パスワードがわかっているかどうかにかかわらず、暗号化されている場合は簡単ではありません。
そのため、暗号化時にmddataおよびmdinfoファイルで使用される暗号化スキームを把握しようとしています。それ以外の場合、これらのファイルの読み取りに問題はなく、そのための堅牢なC#ライブラリを構築しました。 (あなたが助けることができれば、私はあなたがどの言語を使用するか気にしません。それが私がここにいる原則です!)
Apple「iPhone OSエンタープライズ導入ガイド」には、「iTunesのデバイス概要ペインで[iPhoneバックアップの暗号化]オプションを選択することで、デバイスバックアップを暗号化形式で保存できます。ファイルは256-ビットキー。キーはiPhoneキーチェーンに安全に保存されます。」
これはかなり良い手がかりであり、iPhoneのStackoverflowにはいくつかの良い情報があります AES/Rijndael相互運用性 128のキーサイズとCBCモードを使用することを提案します。
他の難読化とは別に、キーと初期化ベクトル(IV)/ saltが必要です。
キーは「バックアップパスワード」の操作であり、ユーザーがiTunesで入力するように求められ、CBCが指示する方法でパディングされた「 AppleMobileBackup.exe 」に渡されると想定するかもしれません。ただし、iPhoneキーチェーンへの参照を考えると、「バックアップパスワード」がX509証明書または対称秘密キーのパスワードとして使用されないのか、証明書または秘密キー自体がキーとして使用されるのか疑問に思います。 ( AES およびiTunesの暗号化/復号化プロセスは対称的です。)
IVは別の問題であり、いくつかの問題が考えられます。おそらく、それは ハードコード化された のキー、または デバイス自体 のキーの1つです。
上記のAppleのコメントは、キーがデバイスのキーチェーンに存在することを示唆していますが、これはそれほど重要ではないと思います。暗号化されたバックアップを 違う これは、復号化に関連するすべての情報がバックアップおよびiTunes構成に存在し、このコンテキストではデバイス上にあるもののみが無関係で置き換え可能であることを示唆しています。キーはどこにあるのでしょうか?
以下にWindowsマシンからのパスをリストしましたが、どのOSを使用していてもそれは非常に重要です。
「\ appdata\Roaming\Apple Computer\iTunes\itunesprefs.xml」には、「Keychain」dictエントリを含むPListが含まれています。 「\ programdata\Apple\Lockdown\09037027da8f4bdefdea97d706703ca034c88bab.plist」には、「DeviceCertificate」、「HostCertificate」、および「RootCertificate」を含むPListが含まれ、これらはすべて有効なX509証明書であるように見えます。同じファイルには、非対称キー「RootPrivateKey」と「HostPrivateKey」も含まれているようです(私の読書では、これらはPKCS#7-envelopedである可能性があります)。また、各バックアップ内のManifest.plistファイルには「AuthSignature」と「AuthData」の値がありますが、これらは各ファイルが増分バックアップされるたびに回転しているように見えますが、実際に何かがなければキーとしては役に立たないことを示唆しています非常に複雑です。
暗号化されたバックアップからデータを取得するのは簡単だと示唆する誤解を招くものがたくさんあります。それはそうではなく、私の知る限りそれは行われていません。 バイパスまたは無効化 バックアップ暗号化は完全に別の問題であり、私がやろうとしていることではありません。
これは、iPhoneやそのようなものを分解することではありません。私がここにいるのは、暗号化されていない場合もあるので、暗号化されたiTunesバックアップからデータ(写真、連絡先など)を抽出する手段だけです。上記の情報を使ってあらゆる種類の順列を試しましたが、どこにも行きませんでした。私が見逃したかもしれない考えやテクニックをいただければ幸いです。
セキュリティ研究者Jean-BaptisteBédruneおよびJean Sigwald これを行う方法を提示 at Hack-in-the-box Amsterdam 2011 。
それ以来、Appleは、キーとアルゴリズムの詳細を含む iOSセキュリティホワイトペーパー をリリースしました。CharlieMiller et al。 iOS Hacker’s Handbook をリリースしました。これは、ハウツー形式で同じ基盤の一部をカバーしています。 iOS 10が最初にリリースされたとき、Appleが最初は公表しなかったバックアップ形式に変更がありましたが、さまざまな人々 形式の変更をリバースエンジニアリングしました 。
暗号化されたiPhoneバックアップの素晴らしい点は、通常の暗号化されていないバックアップにはないWiFiパスワードなどが含まれていることです。 iOS Security Whitepaper で説明されているように、暗号化されたバックアップはより「安全」であると見なされるため、Appleはより機密情報を含めることが問題ないと見なします。
重要な警告:明らかに、iOSデバイスのバックアップを復号化すると、その暗号化は削除されます。あなたのプライバシーとセキュリティを保護するために、これらのスクリプトはフルディスク暗号化を備えたマシンでのみ実行する必要があります。メモリ内のキーを保護するソフトウェア、例えば VirtualLock()
や SecureZeroMemory()
などの関数を使用すると、これらのPythonスクリプトは暗号化キーとパスワードを文字列に保存し、ガベージコレクションの対象にしますPython。これは、秘密鍵とパスワードがしばらくの間RAMに存在し、そこから敵がスワップファイルとディスクに漏洩し、敵がそれらを回復できることを意味します。これは、暗号化されたバックアップを持つという点を完全に無効にします。
iOSセキュリティホワイトペーパー は、ファイルごとのキー、保護クラス、保護クラスキー、キーバッグの基本的な概念を、私ができる以上に説明しています。まだこれらに精通していない場合は、数分かけて関連部分を読んでください。
これで、iOSのすべてのファイルが独自のランダムなファイルごとの暗号化キーで暗号化され、保護クラスに属し、ファイルごとの暗号化キーがファイルシステムメタデータに保存され、保護クラスキーでラップされることがわかりました。
復号化するには:
Manifest.plist
のBackupKeyBag
エントリに保存されているキーバッグをデコードします。この構造の概要は whitepaper に記載されています。 iPhone Wiki は、バイナリ形式を説明しています。4バイトの文字列タイプのフィールド、4バイトのビッグエンディアンの長さフィールド、そして値自体です。
重要な値は、PBKDF2 ITER
ationsおよびSALT
、二重保護ソルトDPSL
、および反復カウントDPIC
、そして各保護CLS
、WPKY
ラップされたキー。
バックアップパスワードを使用すると、正しいPBKDF2ソルトと反復回数を使用して32バイトキーが導出されます。最初にDPSL
およびDPIC
でSHA256ラウンドを使用し、次にITER
およびSALT
でSHA1ラウンドを使用します。
RFC 3394 に従って、ラップされた各キーのラップを解除します。
Manifest.plist
内のManifestKey
から4バイトの保護クラスと長いキーを引き出して展開し、マニフェストデータベースを復号化します。これで、すべてのファイルメタデータを含むSQLiteデータベースが作成されました。
関心のある各ファイルについて、EncryptionKey
およびProtectionClass
エントリを含むバイナリplistのFiles.file
データベース列を見て、クラス暗号化されたファイルごとの暗号化キーと保護クラスコードを取得します。使用する前に、EncryptionKey
から最初の4バイト長のタグを取り除きます。
次に、バックアップパスワードでラップ解除されたクラスキーでラップ解除して、最終的な復号化キーを取得します。次に、IVがゼロのCBCモードでAESを使用してファイルを復号化します。
まず、ライブラリの依存関係が必要になります。 homebrew-installed Python 2.7または3.7を使用してMacを使用している場合、依存関係を次のようにインストールできます。
CFLAGS="-I$(brew --prefix)/opt/openssl/include" \
LDFLAGS="-L$(brew --prefix)/opt/openssl/lib" \
pip install biplist fastpbkdf2 pycrypto
実行可能なソースコード形式で、暗号化されたiPhoneバックアップから単一の設定ファイルを復号化する方法は次のとおりです。
#!/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 __== '__main__':
main()
次に、この出力を印刷します。
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}
BédruneとSigwaldによって投稿された iphone-dataprotection code は、バックアップからkeychainを解読できます。保存されたwifiや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|
...
そのコードは、最新のiOSを使用する電話からのバックアップでは動作しませんが、 golangports がいくつかあり、 keychain 。
申し訳ありませんが、pbkdf2、またはそのバリエーションを含む、より複雑な場合もあります。 WWDC 2010セッション#209を聞いてください。このセッションでは、主にiOS 4のセキュリティ対策について説明していますが、バックアップの個別の暗号化とそれらの関係についても簡単に説明しています。
パスワードを知らなくても、ブルートフォースによっても、パスワードを解読することはできません。
パスワードを知っている人がバックアップのデータにアクセスできるようにしたいとします。
どのアルゴリズムが採用されているかを判断するために、iTunesの実際のコードを見る方法がないのではないかと心配しています。
ニュートン時代には、プログラムからデータを復号化する必要があり、アルゴリズムを理解することなく、復号化関数を直接呼び出すことができました(もちろん、パスワードを知っています)。残念ながら、もう簡単ではありません。
そのiTunesコードをリバースエンジニアリングできる熟練した人がいると確信しています-あなたは彼らに興味を持ってもらうだけです。
理論的には、Appleのアルゴリズムは、正確な暗号化方法を知っている攻撃者に対して、データを安全に(つまり、ブルートフォースの方法で実質的に解読不可能に)するように設計する必要があります。そして、WWDCセッション209では、彼らはこれを達成するために何をするかについて詳細に深く入りました。善意を伝えれば、実際にAppleのセキュリティチームから直接回答を得ることができます。結局のところ、彼らも難読化によるセキュリティは実際には効率的ではないことを知っている必要があります。セキュリティメーリングリストをお試しください。彼らが応答しなくても、おそらくリストに載っている他の誰かが静かに応答します。
幸運を!
試したことはありませんが、Elcomsoftは、フォレンジックの目的で、バックアップを復号化できると主張する製品をリリースしました。ソリューションを自分で設計するほどクールではないかもしれませんが、より高速になる可能性があります。