mirror of
https://github.com/mvt-project/mvt.git
synced 2026-10-30 17:56:25 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e1992a04b |
No files matched your search
@@ -13,7 +13,7 @@ from typing import List, Optional
|
|||||||
|
|
||||||
from mvt.android.artifacts.getprop import GetProp
|
from mvt.android.artifacts.getprop import GetProp
|
||||||
from mvt.android.cmd_check_intrusion_logs import CmdAndroidCheckIntrusionLogs
|
from mvt.android.cmd_check_intrusion_logs import CmdAndroidCheckIntrusionLogs
|
||||||
from mvt.android.cmd_check_backup import CmdAndroidCheckBackup
|
from mvt.android.cmd_check_backup import CmdAndroidCheckBackup, InvalidAndroidBackup
|
||||||
from mvt.android.cmd_check_bugreport import CmdAndroidCheckBugreport
|
from mvt.android.cmd_check_bugreport import CmdAndroidCheckBugreport
|
||||||
from mvt.common.command import Command
|
from mvt.common.command import Command
|
||||||
from mvt.common.indicators import Indicators
|
from mvt.common.indicators import Indicators
|
||||||
@@ -240,7 +240,14 @@ class CmdAndroidCheckAndroidQF(Command):
|
|||||||
hashes=self.hashes,
|
hashes=self.hashes,
|
||||||
sub_command=True,
|
sub_command=True,
|
||||||
)
|
)
|
||||||
cmd.from_ab(backup)
|
try:
|
||||||
|
cmd.from_ab(backup)
|
||||||
|
except InvalidAndroidBackup as exc:
|
||||||
|
self.log.warning(
|
||||||
|
"Skipping backup modules as backup.ab is malformed: %s", exc
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
cmd.run()
|
cmd.run()
|
||||||
|
|
||||||
self.timeline.extend(cmd.timeline)
|
self.timeline.extend(cmd.timeline)
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ from .modules.backup import BACKUP_MODULES
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidAndroidBackup(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CmdAndroidCheckBackup(Command):
|
class CmdAndroidCheckBackup(Command):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -68,6 +72,10 @@ class CmdAndroidCheckBackup(Command):
|
|||||||
self.__type = "ab"
|
self.__type = "ab"
|
||||||
header = parse_ab_header(ab_file_bytes)
|
header = parse_ab_header(ab_file_bytes)
|
||||||
if not header["backup"]:
|
if not header["backup"]:
|
||||||
|
if self.sub_command:
|
||||||
|
raise InvalidAndroidBackup(
|
||||||
|
"Invalid backup format, file should be in .ab format"
|
||||||
|
)
|
||||||
log.critical("Invalid backup format, file should be in .ab format")
|
log.critical("Invalid backup format, file should be in .ab format")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -83,12 +91,25 @@ class CmdAndroidCheckBackup(Command):
|
|||||||
log.critical("Invalid backup password")
|
log.critical("Invalid backup password")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except AndroidBackupParsingError as exc:
|
except AndroidBackupParsingError as exc:
|
||||||
|
if self.sub_command:
|
||||||
|
raise InvalidAndroidBackup(
|
||||||
|
f"Impossible to parse this backup file: {exc}"
|
||||||
|
) from exc
|
||||||
log.critical("Impossible to parse this backup file: %s", exc)
|
log.critical("Impossible to parse this backup file: %s", exc)
|
||||||
log.critical("Please use Android Backup Extractor (ABE) instead")
|
log.critical("Please use Android Backup Extractor (ABE) instead")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
dbytes = io.BytesIO(tardata)
|
dbytes = io.BytesIO(tardata)
|
||||||
self.__tar = tarfile.open(fileobj=dbytes)
|
try:
|
||||||
|
self.__tar = tarfile.open(fileobj=dbytes)
|
||||||
|
except tarfile.TarError as exc:
|
||||||
|
if self.sub_command:
|
||||||
|
raise InvalidAndroidBackup(
|
||||||
|
f"Impossible to parse this backup file: {exc}"
|
||||||
|
) from exc
|
||||||
|
log.critical("Impossible to parse this backup file: %s", exc)
|
||||||
|
log.critical("Please use Android Backup Extractor (ABE) instead")
|
||||||
|
sys.exit(1)
|
||||||
for member in self.__tar:
|
for member in self.__tar:
|
||||||
self.__files.append(member.name)
|
self.__files.append(member.name)
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class SMS(AndroidQFModule):
|
|||||||
def parse_backup(self, data):
|
def parse_backup(self, data):
|
||||||
header = parse_ab_header(data)
|
header = parse_ab_header(data)
|
||||||
if not header["backup"]:
|
if not header["backup"]:
|
||||||
self.log.critical("Invalid backup format, backup.ab was not analysed")
|
self.log.warning("Invalid backup format, backup.ab was not analysed")
|
||||||
return
|
return
|
||||||
|
|
||||||
password = None
|
password = None
|
||||||
@@ -76,7 +76,7 @@ class SMS(AndroidQFModule):
|
|||||||
self.log.critical("Invalid backup password")
|
self.log.critical("Invalid backup password")
|
||||||
return
|
return
|
||||||
except AndroidBackupParsingError:
|
except AndroidBackupParsingError:
|
||||||
self.log.critical(
|
self.log.warning(
|
||||||
"Impossible to parse this backup file, please use"
|
"Impossible to parse this backup file, please use"
|
||||||
" Android Backup Extractor instead"
|
" Android Backup Extractor instead"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,13 +48,16 @@ def parse_ab_header(data):
|
|||||||
'encryption': "none", 'version': 4}
|
'encryption': "none", 'version': 4}
|
||||||
"""
|
"""
|
||||||
if data.startswith(b"ANDROID BACKUP"):
|
if data.startswith(b"ANDROID BACKUP"):
|
||||||
[_, version, is_compressed, encryption, _] = data.split(b"\n", 4)
|
try:
|
||||||
return {
|
[_, version, is_compressed, encryption, _] = data.split(b"\n", 4)
|
||||||
"backup": True,
|
return {
|
||||||
"compression": (is_compressed == b"1"),
|
"backup": True,
|
||||||
"version": int(version),
|
"compression": (is_compressed == b"1"),
|
||||||
"encryption": encryption.decode("utf-8"),
|
"version": int(version),
|
||||||
}
|
"encryption": encryption.decode("utf-8"),
|
||||||
|
}
|
||||||
|
except (UnicodeDecodeError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
return {"backup": False, "compression": None, "version": None, "encryption": None}
|
return {"backup": False, "compression": None, "version": None, "encryption": None}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,24 @@
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from mvt.android.parsers.backup import parse_backup_file, parse_tar_for_sms
|
from mvt.android.parsers.backup import (
|
||||||
|
parse_ab_header,
|
||||||
|
parse_backup_file,
|
||||||
|
parse_tar_for_sms,
|
||||||
|
)
|
||||||
|
|
||||||
from ..utils import get_artifact
|
from ..utils import get_artifact
|
||||||
|
|
||||||
|
|
||||||
class TestBackupParsing:
|
class TestBackupParsing:
|
||||||
|
def test_parse_incomplete_header(self):
|
||||||
|
assert parse_ab_header(b"ANDROID BACKUP\n") == {
|
||||||
|
"backup": False,
|
||||||
|
"compression": None,
|
||||||
|
"version": None,
|
||||||
|
"encryption": None,
|
||||||
|
}
|
||||||
|
|
||||||
def test_parsing_noencryption(self):
|
def test_parsing_noencryption(self):
|
||||||
file = get_artifact("android_backup/backup.ab")
|
file = get_artifact("android_backup/backup.ab")
|
||||||
with open(file, "rb") as f:
|
with open(file, "rb") as f:
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
|
||||||
@@ -68,3 +70,18 @@ class TestCheckAndroidqfCommand:
|
|||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
del os.environ["MVT_ANDROID_BACKUP_PASSWORD"]
|
del os.environ["MVT_ANDROID_BACKUP_PASSWORD"]
|
||||||
settings.__init__() # Reset settings
|
settings.__init__() # Reset settings
|
||||||
|
|
||||||
|
def test_check_malformed_backup_skips_backup_modules(self, tmp_path, caplog):
|
||||||
|
path = tmp_path / "androidqf"
|
||||||
|
shutil.copytree(os.path.join(get_artifact_folder(), "androidqf"), path)
|
||||||
|
(path / "backup.ab").write_bytes(b"")
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
result = runner.invoke(check_androidqf, [str(path)])
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "Skipping backup modules as backup.ab is malformed" in caplog.text
|
||||||
|
assert not any(
|
||||||
|
record.levelname in {"CRITICAL", "FATAL"} for record in caplog.records
|
||||||
|
)
|
||||||
Reference in new issue
Block a user