Rework old detections tracking into stuctured alert levels

This commit is contained in:
Donncha Ó Cearbhaill
2025-02-19 23:46:03 +01:00
parent ca0bc46f11
commit 2d547662f8
126 changed files with 1132 additions and 761 deletions
@@ -49,6 +49,6 @@ class TestDumpsysAccessibilityArtifact:
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.sec.android.app.camera")
da.indicators = ind
assert len(da.detected) == 0
assert len(da.alertstore.alerts) == 0
da.check_indicators()
assert len(da.detected) == 1
assert len(da.alertstore.alerts) == 1
@@ -42,22 +42,24 @@ class TestDumpsysAppopsArtifact:
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.facebook.katana")
da.indicators = ind
assert len(da.detected) == 0
assert len(da.alertstore.alerts) == 0
da.check_indicators()
detected_by_ioc = [
detected for detected in da.detected if detected.get("matched_indicator")
alert
for alert in da.alertstore.alerts
if "matched_indicator" in alert.event
]
detected_by_permission_heuristic = [
detected
for detected in da.detected
alert
for alert in da.alertstore.alerts
if all(
[
perm["name"] == "REQUEST_INSTALL_PACKAGES"
for perm in detected["permissions"]
for perm in alert.event["permissions"]
]
)
]
assert len(da.detected) == 3
assert len(da.alertstore.alerts) == 3
assert len(detected_by_ioc) == 1
assert len(detected_by_permission_heuristic) == 2
@@ -32,6 +32,6 @@ class TestDumpsysBatteryDailyArtifact:
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.facebook.system")
dba.indicators = ind
assert len(dba.detected) == 0
assert len(dba.alertstore.alerts) == 0
dba.check_indicators()
assert len(dba.detected) == 1
assert len(dba.alertstore.alerts) == 1
@@ -39,6 +39,6 @@ class TestDumpsysBatteryHistoryArtifact:
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.samsung.android.app.reminder")
dba.indicators = ind
assert len(dba.detected) == 0
assert len(dba.alertstore.alerts) == 0
dba.check_indicators()
assert len(dba.detected) == 2
assert len(dba.alertstore.alerts) == 2
@@ -37,6 +37,6 @@ class TestDumpsysDBinfoArtifact:
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.wssyncmldm")
dbi.indicators = ind
assert len(dbi.detected) == 0
assert len(dbi.alertstore.alerts) == 0
dbi.check_indicators()
assert len(dbi.detected) == 5
assert len(dbi.alertstore.alerts) == 5
@@ -39,6 +39,6 @@ class TestDumpsysPackageActivitiesArtifact:
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.google.android.gms")
dpa.indicators = ind
assert len(dpa.detected) == 0
assert len(dpa.alertstore.alerts) == 0
dpa.check_indicators()
assert len(dpa.detected) == 1
assert len(dpa.alertstore.alerts) == 1
@@ -37,6 +37,6 @@ class TestDumpsysPackagesArtifact:
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.sec.android.app.DataCreate")
dpa.indicators = ind
assert len(dpa.detected) == 0
assert len(dpa.alertstore.alerts) == 0
dpa.check_indicators()
assert len(dpa.detected) == 1
assert len(dpa.alertstore.alerts) == 1
@@ -35,6 +35,6 @@ class TestDumpsysPlatformCompatArtifact:
ind.ioc_collections[0]["app_ids"].append("org.torproject.torbrowser")
ind.ioc_collections[0]["app_ids"].append("org.article19.circulo.next")
dbi.indicators = ind
assert len(dbi.detected) == 0
assert len(dbi.alertstore.alerts) == 0
dbi.check_indicators()
assert len(dbi.detected) == 2
assert len(dbi.alertstore.alerts) == 2
@@ -42,6 +42,6 @@ class TestDumpsysReceiversArtifact:
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["app_ids"].append("com.android.storagemanager")
dr.indicators = ind
assert len(dr.detected) == 0
assert len(dr.alertstore.alerts) == 0
dr.check_indicators()
assert len(dr.detected) == 1
assert len(dr.alertstore.alerts) == 1
+2 -2
View File
@@ -36,6 +36,6 @@ class TestGetPropArtifact:
"dalvik.vm.appimageformat"
)
gp.indicators = ind
assert len(gp.detected) == 0
assert len(gp.alertstore.alerts) == 0
gp.check_indicators()
assert len(gp.detected) == 1
assert len(gp.alertstore.alerts) == 1
+2 -2
View File
@@ -33,6 +33,6 @@ class TestProcessesArtifact:
ind.parse_stix2(indicator_file)
ind.ioc_collections[0]["processes"].append("lru-add-drain")
p.indicators = ind
assert len(p.detected) == 0
assert len(p.alertstore.alerts) == 0
p.check_indicators()
assert len(p.detected) == 1
assert len(p.alertstore.alerts) == 1
-1
View File
@@ -60,7 +60,6 @@ class TestBackupParsing:
== "33e73df2ede9798dcb3a85c06200ee41c8f52dd2f2e50ffafcceb0407bc13e3a"
)
sms = parse_tar_for_sms(ddata)
print(sms)
assert isinstance(sms, list)
assert len(sms) == 1
assert len(sms[0]["links"]) == 1
+1 -1
View File
@@ -22,4 +22,4 @@ class TestAndroidqfFilesAnalysis:
run_module(m)
assert len(m.results) == 3
assert len(m.timeline) == 6
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
+4 -4
View File
@@ -26,7 +26,7 @@ class TestAndroidqfGetpropAnalysis:
assert m.results[0]["name"] == "dalvik.vm.appimageformat"
assert m.results[0]["value"] == "lz4"
assert len(m.timeline) == 0
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
def test_getprop_parsing_zip(self):
fpath = get_artifact("androidqf.zip")
@@ -38,7 +38,7 @@ class TestAndroidqfGetpropAnalysis:
assert m.results[0]["name"] == "dalvik.vm.appimageformat"
assert m.results[0]["value"] == "lz4"
assert len(m.timeline) == 0
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
def test_androidqf_getprop_detection(self, indicator_file):
data_path = get_android_androidqf()
@@ -52,5 +52,5 @@ class TestAndroidqfGetpropAnalysis:
m.indicators = ind
run_module(m)
assert len(m.results) == 10
assert len(m.detected) == 1
assert m.detected[0]["name"] == "dalvik.vm.heapmaxfree"
assert len(m.alertstore.alerts) == 1
assert m.alertstore.alerts[0].event["name"] == "dalvik.vm.heapmaxfree"
+32 -29
View File
@@ -47,38 +47,35 @@ class TestAndroidqfPackages:
def test_non_appstore_warnings(self, caplog, module):
run_module(module)
assert len(module.detected) == 4
assert len(module.alertstore.alerts) == 5
# 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
)
adb_message = "Found a non-system package installed via adb or another method:"
whatsapp_detected = [
pkg for pkg in module.detected if pkg["name"] == "com.whatsapp"
alert
for alert in module.alertstore.alerts
if alert.event["name"] == "com.whatsapp"
]
assert len(whatsapp_detected) == 1
assert adb_message in whatsapp_detected[0].message
assert (
'Found a package installed via a browser (installer="com.google.android.packageinstaller"): '
'"app.revanced.manager.flutter"' in caplog.text
)
browser_message = 'Found a package installed via a browser (installer="com.google.android.packageinstaller"): '
revanced_detected = [
pkg
for pkg in module.detected
if pkg["name"] == "app.revanced.manager.flutter"
alert
for alert in module.alertstore.alerts
if alert.event["name"] == "app.revanced.manager.flutter"
]
assert len(revanced_detected) == 1
assert browser_message in revanced_detected[0].message
assert (
'Found a package installed via a third party store (installer="org.fdroid.fdroid"): "org.nuclearfog.apollo"'
in caplog.text
)
# We do not currently flag a third party store as a detection, we only flag the app in the logs.
third_party_message = 'Found a package installed via a third party store (installer="org.fdroid.fdroid")'
appollo_detected = [
pkg for pkg in module.detected if pkg["name"] == "org.nuclearfog.apollo"
alert
for alert in module.alertstore.alerts
if alert.event["name"] == "org.nuclearfog.apollo"
]
assert len(appollo_detected) == 0
assert len(appollo_detected) == 1
assert third_party_message in appollo_detected[0].message
def test_packages_ioc_package_names(self, module, indicators_factory):
module.indicators = indicators_factory(app_ids=["com.malware.blah"])
@@ -86,12 +83,14 @@ class TestAndroidqfPackages:
run_module(module)
possible_detected_app = [
pkg for pkg in module.detected if pkg["name"] == "com.malware.blah"
alert
for alert in module.alertstore.alerts
if alert.event["name"] == "com.malware.blah"
]
assert len(possible_detected_app) == 1
assert possible_detected_app[0]["name"] == "com.malware.blah"
assert possible_detected_app[0].event["name"] == "com.malware.blah"
assert (
possible_detected_app[0]["matched_indicator"].ioc.value
possible_detected_app[0].event["matched_indicator"].value
== "com.malware.blah"
)
@@ -105,12 +104,14 @@ class TestAndroidqfPackages:
run_module(module)
possible_detected_app = [
pkg for pkg in module.detected if pkg["name"] == "com.malware.muahaha"
alert
for alert in module.alertstore.alerts
if alert.event["name"] == "com.malware.muahaha"
]
assert len(possible_detected_app) == 1
assert possible_detected_app[0]["name"] == "com.malware.muahaha"
assert possible_detected_app[0].event["name"] == "com.malware.muahaha"
assert (
possible_detected_app[0]["matched_indicator"].ioc.value
possible_detected_app[0].event["matched_indicator"].value
== "31037a27af59d4914906c01ad14a318eee2f3e31d48da8954dca62a99174e3fa"
)
@@ -124,11 +125,13 @@ class TestAndroidqfPackages:
run_module(module)
possible_detected_app = [
pkg for pkg in module.detected if pkg["name"] == "com.malware.muahaha"
alert
for alert in module.alertstore.alerts
if alert.event["name"] == "com.malware.muahaha"
]
assert len(possible_detected_app) == 1
assert possible_detected_app[0]["name"] == "com.malware.muahaha"
assert possible_detected_app[0].event["name"] == "com.malware.muahaha"
assert (
possible_detected_app[0]["matched_indicator"].ioc.value
possible_detected_app[0].event["matched_indicator"].value
== "c7e56178748be1441370416d4c10e34817ea0c961eb636c8e9d98e0fd79bf730"
)
+1 -1
View File
@@ -22,4 +22,4 @@ class TestAndroidqfProcessesAnalysis:
run_module(m)
assert len(m.results) == 15
assert len(m.timeline) == 0
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
+1 -1
View File
@@ -21,4 +21,4 @@ class TestSettingsModule:
run_module(m)
assert len(m.results) == 1
assert "random" in m.results.keys()
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
+1 -1
View File
@@ -25,7 +25,7 @@ class TestAndroidqfSMSAnalysis:
run_module(m)
assert len(m.results) == 2
assert len(m.timeline) == 0
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
def test_androidqf_sms_encrypted_password_valid(self):
data_path = os.path.join(get_artifact_folder(), "androidqf_encrypted")
+36
View File
@@ -0,0 +1,36 @@
# 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 mvt.common.indicators import Indicators
from mvt.common.module import run_module
from mvt.ios.modules.mixed.tcc import TCC
from ..utils import get_ios_backup_folder
class TestTCCModule:
def test_tcc(self):
m = TCC(target_path=get_ios_backup_folder())
run_module(m)
assert len(m.results) == 11
assert len(m.timeline) == 11
assert len(m.alertstore.alerts) == 0
assert m.results[0]["service"] == "kTCCServiceUbiquity"
assert m.results[0]["client"] == "com.apple.Preferences"
assert m.results[0]["auth_value"] == "allowed"
def test_tcc_detection(self, indicator_file):
m = TCC(target_path=get_ios_backup_folder())
ind = Indicators(log=logging.getLogger())
ind.parse_stix2(indicator_file)
m.indicators = ind
run_module(m)
assert len(m.results) == 11
assert len(m.timeline) == 11
assert len(m.alertstore.alerts) == 1
assert m.alertstore.alerts[0].event["service"] == "kTCCServiceLiverpool"
assert m.alertstore.alerts[0].event["client"] == "Launch"
+6 -2
View File
@@ -35,9 +35,13 @@ class TestBugreportAnalysis:
assert len(m.timeline) == 16
detected_by_ioc = [
detected for detected in m.detected if detected.get("matched_indicator")
detected
for detected in m.alertstore.alerts
if detected.event.get("matched_indicator")
]
assert len(m.detected) == 1 # Hueristic detection for suspicious permissions
assert (
len(m.alertstore.alerts) == 1
) # Hueristic detection for suspicious permissions
assert len(detected_by_ioc) == 0
def test_packages_module(self):
+2 -2
View File
@@ -18,7 +18,7 @@ class TestCalendarModule:
run_module(m)
assert len(m.results) == 1
assert len(m.timeline) == 4
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
assert m.results[0]["summary"] == "Super interesting meeting"
def test_calendar_detection(self, indicator_file):
@@ -30,4 +30,4 @@ class TestCalendarModule:
run_module(m)
assert len(m.results) == 1
assert len(m.timeline) == 4
assert len(m.detected) == 1
assert len(m.alertstore.alerts) == 1
+8 -2
View File
@@ -7,6 +7,7 @@ import logging
from mvt.common.indicators import Indicators
from mvt.common.module import run_module
from mvt.common.alerts import AlertLevel
from mvt.ios.modules.mixed.net_datausage import Datausage
from ..utils import get_ios_backup_folder
@@ -19,7 +20,9 @@ class TestDatausageModule:
assert m.results[0]["isodate"][0:19] == "2019-08-27 15:08:09"
assert len(m.results) == 42
assert len(m.timeline) == 60
assert len(m.detected) == 0
assert (
len(m.alertstore.alerts) == 1
) # We now have a detection for missing processes.
def test_detection(self, indicator_file):
m = Datausage(target_path=get_ios_backup_folder())
@@ -29,4 +32,7 @@ class TestDatausageModule:
ind.ioc_collections[0]["processes"].append("CumulativeUsageTracker")
m.indicators = ind
run_module(m)
assert len(m.detected) == 2
critical_alerts = [
alert for alert in m.alertstore.alerts if alert.level == AlertLevel.CRITICAL
]
assert len(critical_alerts) == 2
+7 -1
View File
@@ -4,6 +4,7 @@
# https://license.mvt.re/1.1/
from mvt.common.module import run_module
from mvt.common.alerts import AlertLevel
from mvt.ios.modules.mixed.global_preferences import GlobalPreferences
from ..utils import get_ios_backup_folder
@@ -15,6 +16,11 @@ class TestGlobalPreferencesModule:
run_module(m)
assert len(m.results) == 16
assert len(m.timeline) == 0
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 1
lockdown_mode_alert = m.alertstore.alerts[0]
assert lockdown_mode_alert.message == "Lockdown mode enabled"
assert lockdown_mode_alert.level == AlertLevel.INFORMATIONAL
assert m.results[0]["entry"] == "WebKitShowLinkPreviews"
assert m.results[0]["value"] is False
+2 -2
View File
@@ -18,7 +18,7 @@ class TestManifestModule:
run_module(m)
assert len(m.results) == 3721
assert len(m.timeline) == 5881
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
def test_detection(self, indicator_file):
m = Manifest(target_path=get_ios_backup_folder())
@@ -27,4 +27,4 @@ class TestManifestModule:
ind.ioc_collections[0]["file_names"].append("com.apple.CoreBrightness.plist")
m.indicators = ind
run_module(m)
assert len(m.detected) == 1
assert len(m.alertstore.alerts) == 1
+2 -2
View File
@@ -19,7 +19,7 @@ class TestSafariBrowserStateModule:
run_module(m)
assert len(m.results) == 1
assert len(m.timeline) == 1
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
def test_detection(self, indicator_file):
m = SafariBrowserState(target_path=get_ios_backup_folder())
@@ -30,6 +30,6 @@ class TestSafariBrowserStateModule:
ind.ioc_collections[0]["domains"].append("en.wikipedia.org")
m.indicators = ind
run_module(m)
assert len(m.detected) == 1
assert len(m.alertstore.alerts) == 1
assert len(m.results) == 1
assert m.results[0]["tab_url"] == "https://en.wikipedia.org/wiki/NSO_Group"
+2 -2
View File
@@ -18,7 +18,7 @@ class TestSMSModule:
run_module(m)
assert len(m.results) == 1
assert len(m.timeline) == 2
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
def test_detection(self, indicator_file):
m = SMS(target_path=get_ios_backup_folder())
@@ -28,4 +28,4 @@ class TestSMSModule:
ind.ioc_collections[0]["domains"].append("badbadbad.example.org")
m.indicators = ind
run_module(m)
assert len(m.detected) == 1
assert len(m.alertstore.alerts) == 1
+4 -4
View File
@@ -18,7 +18,7 @@ class TestTCCModule:
run_module(m)
assert len(m.results) == 11
assert len(m.timeline) == 11
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
assert m.results[0]["service"] == "kTCCServiceUbiquity"
assert m.results[0]["client"] == "com.apple.Preferences"
assert m.results[0]["auth_value"] == "allowed"
@@ -31,6 +31,6 @@ class TestTCCModule:
run_module(m)
assert len(m.results) == 11
assert len(m.timeline) == 11
assert len(m.detected) == 1
assert m.detected[0]["service"] == "kTCCServiceLiverpool"
assert m.detected[0]["client"] == "Launch"
assert len(m.alertstore.alerts) == 1
assert m.alertstore.alerts[0].event["service"] == "kTCCServiceLiverpool"
assert m.alertstore.alerts[0].event["client"] == "Launch"
@@ -18,4 +18,4 @@ class TestWebkitResourceLoadStatisticsModule:
run_module(m)
assert len(m.results) == 2
assert len(m.timeline) == 2
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
+2 -2
View File
@@ -17,7 +17,7 @@ class TestFilesystem:
run_module(m)
assert len(m.results) == 15
assert len(m.timeline) == 15
assert len(m.detected) == 0
assert len(m.alertstore.alerts) == 0
def test_detection(self, indicator_file):
m = Filesystem(target_path=get_ios_backup_folder())
@@ -31,4 +31,4 @@ class TestFilesystem:
run_module(m)
assert len(m.results) == 15
assert len(m.timeline) == 15
assert len(m.detected) == 1
assert len(m.alertstore.alerts) == 1