-
Notifications
You must be signed in to change notification settings - Fork 986
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
461 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# Mobile Verification Toolkit (MVT) | ||
# Copyright (c) 2021-2023 The MVT Authors. | ||
# Use of this software is governed by the MVT License 1.1 that can be found at | ||
# https://license.mvt.re/1.1/ | ||
|
||
import logging | ||
from typing import Optional | ||
import json | ||
|
||
from mvt.android.utils import ( | ||
ROOT_PACKAGES, | ||
BROWSER_INSTALLERS, | ||
PLAY_STORE_INSTALLERS, | ||
THIRD_PARTY_STORE_INSTALLERS, | ||
) | ||
|
||
from .base import AndroidQFModule | ||
|
||
|
||
class Packages(AndroidQFModule): | ||
"""This module examines the installed packages in packages.json""" | ||
|
||
def __init__( | ||
self, | ||
file_path: Optional[str] = None, | ||
target_path: Optional[str] = None, | ||
results_path: Optional[str] = None, | ||
module_options: Optional[dict] = None, | ||
log: logging.Logger = logging.getLogger(__name__), | ||
results: Optional[list] = None, | ||
) -> None: | ||
super().__init__( | ||
file_path=file_path, | ||
target_path=target_path, | ||
results_path=results_path, | ||
module_options=module_options, | ||
log=log, | ||
results=results, | ||
) | ||
|
||
def check_indicators(self) -> None: | ||
for result in self.results: | ||
if result["name"] in ROOT_PACKAGES: | ||
self.log.warning( | ||
"Found an installed package related to " | ||
'rooting/jailbreaking: "%s"', | ||
result["name"], | ||
) | ||
self.detected.append(result) | ||
continue | ||
|
||
# Detections for apps installed via unusual methods | ||
if result["installer"] in THIRD_PARTY_STORE_INSTALLERS: | ||
self.log.warning( | ||
'Found a package installed via a third party store (installer="%s"): "%s"', | ||
result["installer"], | ||
result["name"], | ||
) | ||
elif result["installer"] in BROWSER_INSTALLERS: | ||
self.log.warning( | ||
'Found a package installed via a browser (installer="%s"): "%s"', | ||
result["installer"], | ||
result["name"], | ||
) | ||
elif result["installer"] == "null" and result["system"] is False: | ||
self.log.warning( | ||
'Found a non-system package installed via adb or another method: "%s"', | ||
result["name"], | ||
) | ||
elif result["installer"] in PLAY_STORE_INSTALLERS: | ||
pass | ||
|
||
if not self.indicators: | ||
continue | ||
|
||
ioc = self.indicators.check_app_id(result.get("name")) | ||
if ioc: | ||
result["matched_indicator"] = ioc | ||
self.detected.append(result) | ||
continue | ||
|
||
for package_file in result.get("files", []): | ||
ioc = self.indicators.check_file_hash(package_file["sha256"]) | ||
if ioc: | ||
result["matched_indicator"] = ioc | ||
self.detected.append(result) | ||
|
||
def run(self) -> None: | ||
packages = self._get_files_by_pattern("*/packages.json") | ||
if not packages: | ||
self.log.error( | ||
"packages.json file not found in this androidqf bundle. Possibly malformed?" | ||
) | ||
return | ||
|
||
self.results = json.loads(self._get_file_content(packages[0])) | ||
self.log.info("Found %d packages in packages.json", len(self.results)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# Mobile Verification Toolkit (MVT) | ||
# Copyright (c) 2021-2023 The MVT Authors. | ||
# Use of this software is governed by the MVT License 1.1 that can be found at | ||
# https://license.mvt.re/1.1/ | ||
|
||
import logging | ||
import pytest | ||
from pathlib import Path | ||
|
||
from mvt.android.modules.androidqf.packages import Packages | ||
from mvt.common.module import run_module | ||
|
||
from ..utils import get_android_androidqf, list_files | ||
|
||
|
||
@pytest.fixture() | ||
def data_path(): | ||
return get_android_androidqf() | ||
|
||
|
||
@pytest.fixture() | ||
def parent_data_path(data_path): | ||
return Path(data_path).absolute().parent.as_posix() | ||
|
||
|
||
@pytest.fixture() | ||
def file_list(data_path): | ||
return list_files(data_path) | ||
|
||
|
||
@pytest.fixture() | ||
def module(parent_data_path, file_list): | ||
m = Packages(target_path=parent_data_path, log=logging) | ||
m.from_folder(parent_data_path, file_list) | ||
return m | ||
|
||
|
||
class TestAndroidqfPackages: | ||
def test_packages_list(self, module): | ||
run_module(module) | ||
|
||
# There should just be 7 packages listed, no detections | ||
assert len(module.results) == 7 | ||
assert len(module.timeline) == 0 | ||
assert len(module.detected) == 0 | ||
|
||
def test_non_appstore_warnings(self, caplog, module): | ||
run_module(module) | ||
|
||
# Not a super test to be searching logs for this but heuristic detections not yet formalised | ||
assert ( | ||
'Found a non-system package installed via adb or another method: "com.whatsapp"' | ||
in caplog.text | ||
) | ||
assert ( | ||
'Found a package installed via a browser (installer="com.google.android.packageinstaller"): ' | ||
'"app.revanced.manager.flutter"' in caplog.text | ||
) | ||
assert ( | ||
'Found a package installed via a third party store (installer="org.fdroid.fdroid"): "org.nuclearfog.apollo"' | ||
in caplog.text | ||
) | ||
|
||
def test_packages_ioc_package_names(self, module, indicators_factory): | ||
module.indicators = indicators_factory(app_ids=["com.malware.blah"]) | ||
|
||
run_module(module) | ||
|
||
assert len(module.detected) == 1 | ||
assert module.detected[0]["name"] == "com.malware.blah" | ||
assert module.detected[0]["matched_indicator"]["value"] == "com.malware.blah" | ||
|
||
def test_packages_ioc_sha256(self, module, indicators_factory): | ||
module.indicators = indicators_factory( | ||
files_sha256=[ | ||
"31037a27af59d4914906c01ad14a318eee2f3e31d48da8954dca62a99174e3fa" | ||
] | ||
) | ||
|
||
run_module(module) | ||
|
||
assert len(module.detected) == 1 | ||
assert module.detected[0]["name"] == "com.malware.muahaha" | ||
assert ( | ||
module.detected[0]["matched_indicator"]["value"] | ||
== "31037a27af59d4914906c01ad14a318eee2f3e31d48da8954dca62a99174e3fa" | ||
) |
Oops, something went wrong.