Refactored to include functionality in existing DecryptBackup class

This commit is contained in:
Nex 2021-07-30 18:46:45 +02:00
parent bfcfb3aa06
commit b264ae946d
3 changed files with 42 additions and 70 deletions

View File

@ -17,7 +17,6 @@ from mvt.common.module import run_module, save_timeline
from mvt.common.options import MutuallyExclusiveOption
from .decrypt import DecryptBackup
from .keyutils import KeyUtils
from .modules.fs import BACKUP_MODULES, FS_MODULES
# Setup logging using Rich.
@ -55,6 +54,7 @@ def cli():
@click.argument("BACKUP_PATH", type=click.Path(exists=True))
def decrypt_backup(destination, password, key_file, backup_path):
backup = DecryptBackup(backup_path, destination)
if password:
backup.decrypt_with_password(password)
elif key_file:
@ -63,13 +63,16 @@ def decrypt_backup(destination, password, key_file, backup_path):
raise click.ClickException("Missing required option. Specify either "
"--password or --key-file.")
backup.process_backup()
#==============================================================================
# Command: extract-key
#==============================================================================
@cli.command("extract-key", help="Extract decryption key from an iTunes backup")
@click.option("--password", "-p",
help="Password to use to decrypt the backup",
prompt="Backup password",
prompt="Enter backup password",
hide_input=True, prompt_required=False, required=True)
@click.option("--key-file", "-k",
help="Key file to be written (if unset, will print to STDOUT)",
@ -77,11 +80,12 @@ def decrypt_backup(destination, password, key_file, backup_path):
type=click.Path(exists=False, file_okay=True, dir_okay=False, writable=True))
@click.argument("BACKUP_PATH", type=click.Path(exists=True))
def extract_key(password, backup_path, key_file):
key_utils = KeyUtils(password, backup_path)
backup = DecryptBackup(backup_path)
backup.decrypt_with_password(password)
backup.get_key()
if key_file:
key_utils.write_key(key_file)
else:
key_utils.print_key()
backup.write_key(key_file)
#==============================================================================

View File

@ -18,7 +18,7 @@ class DecryptBackup:
using either a password or a key file.
"""
def __init__(self, backup_path, dest_path):
def __init__(self, backup_path, dest_path=None):
"""Decrypts an encrypted iOS backup.
:param backup_path: Path to the encrypted backup folder
:param dest_path: Path to the folder where to store the decrypted backup
@ -26,8 +26,12 @@ class DecryptBackup:
self.backup_path = backup_path
self.dest_path = dest_path
self._backup = None
self._decryption_key = None
def process_backup(self):
if not os.path.exists(self.dest_path):
os.makedirs(self.dest_path)
def _process_backup(self):
manifest_path = os.path.join(self.dest_path, "Manifest.db")
# We extract a decrypted Manifest.db.
self._backup.getManifestDB()
@ -69,9 +73,6 @@ class DecryptBackup:
"""
log.info("Decrypting iOS backup at path %s with password", self.backup_path)
if not os.path.exists(self.dest_path):
os.makedirs(self.dest_path)
try:
self._backup = iOSbackup(udid=os.path.basename(self.backup_path),
cleartextpassword=password,
@ -79,9 +80,6 @@ class DecryptBackup:
except Exception as e:
log.exception(e)
log.critical("Failed to decrypt backup. Did you provide the correct password?")
return
else:
self._process_backup()
def decrypt_with_key_file(self, key_file):
"""Decrypts an encrypted iOS backup using a key file.
@ -90,9 +88,6 @@ class DecryptBackup:
log.info("Decrypting iOS backup at path %s with key file %s",
self.backup_path, key_file)
if not os.path.exists(self.dest_path):
os.makedirs(self.dest_path)
with open(key_file, "rb") as handle:
key_bytes = handle.read()
@ -109,6 +104,31 @@ class DecryptBackup:
except Exception as e:
log.exception(e)
log.critical("Failed to decrypt backup. Did you provide the correct key file?")
def get_key(self):
"""Retrieve and prints the encryption key.
"""
if not self._backup:
return
self._decryption_key = self._backup.getDecryptionKey()
log.info("Derived decryption key for backup at path %s is: \"%s\"",
self.backup_path, self._decryption_key)
def write_key(self, key_path):
"""Save extracted key to file.
:param key_path: Path to the file where to write the derived decryption key.
"""
if not self._decryption_key:
return
try:
with open(key_path, 'w') as handle:
handle.write(self._decryption_key)
except Exception as e:
log.exception(e)
log.critical("Failed to write key to file: %s", key_path)
return
else:
self._process_backup()
log.info("Wrote decryption key to file: %s. This file is equivalent to a plaintext password. Keep it safe!",
key_path)

View File

@ -1,52 +0,0 @@
import os
import logging
from iOSbackup import iOSbackup
log = logging.getLogger(__name__)
class KeyUtils:
"""This class provides functions to extract a backup key from a password.
"""
def __init__(self, password, backup_path):
"""Generates a key file for an iOS backup.
:param password: Backup encryption password
:param key_file: Path to the file where to store the generated key file
"""
self.password = password
self.backup_path = backup_path
self._backup = None
def get_key(self):
try:
self._backup = iOSbackup(udid=os.path.basename(self.backup_path),
cleartextpassword=self.password,
backuproot=os.path.dirname(self.backup_path))
except Exception as e:
log.exception(e)
log.critical("Failed to decrypt backup. Did you provide the correct password?")
return
else:
self.decryption_key = self._backup.getDecryptionKey()
log.info("Extracted decryption key.")
def print_key(self):
self.get_key()
log.info("Decryption key for backup at path %s is:\n %s",
self.backup_path, self.decryption_key)
def write_key(self, key_file):
self.get_key()
try:
with open(key_file, 'w') as writer:
writer.write(self.decryption_key)
except Exception as e:
log.exception(e)
log.critical("Failed to write key file.")
return
else:
log.info("Wrote decryption key for backup at path %s to file %s",
self.backup_path, key_file)
log.warn("The file %s is equivalent to a plaintext password. Keep this file safe!",
key_file)