mirror of
https://github.com/mvt-project/mvt.git
synced 2024-06-26 14:28:54 +00:00
Adds indicators for android properties
This commit is contained in:
parent
70c6f0c153
commit
b5d7e528de
|
@ -30,6 +30,16 @@ class Getprop(AndroidExtraction):
|
|||
|
||||
self.results = {} if not results else results
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
if not self.indicators:
|
||||
return
|
||||
|
||||
for result in self.results:
|
||||
ioc = self.indicators.check_android_property_name(result.get("name", ""))
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
|
||||
def run(self) -> None:
|
||||
self._adb_connect()
|
||||
output = self._adb_command("getprop")
|
||||
|
@ -38,13 +48,14 @@ class Getprop(AndroidExtraction):
|
|||
self.results = parse_getprop(output)
|
||||
|
||||
# Alert if phone is outdated.
|
||||
security_patch = self.results.get("ro.build.version.security_patch", "")
|
||||
if security_patch:
|
||||
patch_date = datetime.strptime(security_patch, "%Y-%m-%d")
|
||||
for entry in self.results:
|
||||
if entry.get("name", "") != "ro.build.version.security_patch":
|
||||
continue
|
||||
patch_date = datetime.strptime(entry["value"], "%Y-%m-%d")
|
||||
if (datetime.now() - patch_date) > timedelta(days=6*30):
|
||||
self.log.warning("This phone has not received security updates "
|
||||
"for more than six months (last update: %s)",
|
||||
security_patch)
|
||||
entry["value"])
|
||||
|
||||
self.log.info("Extracted %d Android system properties",
|
||||
len(self.results))
|
||||
|
|
|
@ -7,7 +7,7 @@ import logging
|
|||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
from mvt.android.parsers import getprop
|
||||
from mvt.android.parsers.getprop import parse_getprop
|
||||
|
||||
from .base import AndroidQFModule
|
||||
|
||||
|
@ -41,7 +41,17 @@ class Getprop(AndroidQFModule):
|
|||
super().__init__(file_path=file_path, target_path=target_path,
|
||||
results_path=results_path, fast_mode=fast_mode,
|
||||
log=log, results=results)
|
||||
self.results = {}
|
||||
self.results = []
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
if not self.indicators:
|
||||
return
|
||||
|
||||
for result in self.results:
|
||||
ioc = self.indicators.check_android_property_name(result.get("name", ""))
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
|
||||
def run(self) -> None:
|
||||
getprop_files = self._get_files_by_pattern("*/getprop.txt")
|
||||
|
@ -52,15 +62,15 @@ class Getprop(AndroidQFModule):
|
|||
with open(getprop_files[0]) as f:
|
||||
data = f.read()
|
||||
|
||||
self.results = getprop.parse_getprop(data)
|
||||
self.results = parse_getprop(data)
|
||||
for entry in self.results:
|
||||
if entry in INTERESTING_PROPERTIES:
|
||||
self.log.info("%s: %s", entry, self.results[entry])
|
||||
if entry == "ro.build.version.security_patch":
|
||||
last_patch = datetime.strptime(self.results[entry], "%Y-%m-%d")
|
||||
if entry["name"] in INTERESTING_PROPERTIES:
|
||||
self.log.info("%s: %s", entry["name"], entry["value"])
|
||||
if entry["name"] == "ro.build.version.security_patch":
|
||||
last_patch = datetime.strptime(entry["value"], "%Y-%m-%d")
|
||||
if (datetime.now() - last_patch) > timedelta(days=6*31):
|
||||
self.log.warning("This phone has not received security "
|
||||
"updates for more than six months "
|
||||
"(last update: %s)", self.results[entry])
|
||||
"(last update: %s)", entry["value"])
|
||||
|
||||
self.log.info("Extracted a total of %d properties", len(self.results))
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
# https://license.mvt.re/1.1/
|
||||
|
||||
import re
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
def parse_getprop(output: str) -> dict:
|
||||
results = {}
|
||||
def parse_getprop(output: str) -> List[Dict[str, str]]:
|
||||
results = []
|
||||
rxp = re.compile(r"\[(.+?)\]: \[(.+?)\]")
|
||||
|
||||
for line in output.splitlines():
|
||||
|
@ -19,8 +20,10 @@ def parse_getprop(output: str) -> dict:
|
|||
if not matches or len(matches[0]) != 2:
|
||||
continue
|
||||
|
||||
key = matches[0][0]
|
||||
value = matches[0][1]
|
||||
results[key] = value
|
||||
entry = {
|
||||
"name": matches[0][0],
|
||||
"value": matches[0][1]
|
||||
}
|
||||
results.append(entry)
|
||||
|
||||
return results
|
||||
|
|
|
@ -72,6 +72,7 @@ class Indicators:
|
|||
"files_sha256": [],
|
||||
"app_ids": [],
|
||||
"ios_profile_ids": [],
|
||||
"android_property_names": [],
|
||||
"count": 0,
|
||||
}
|
||||
|
||||
|
@ -121,6 +122,11 @@ class Indicators:
|
|||
ioc_coll=collection,
|
||||
ioc_coll_list=collection["ios_profile_ids"])
|
||||
|
||||
elif key == "android-property:name":
|
||||
self._add_indicator(ioc=value,
|
||||
ioc_coll=collection,
|
||||
ioc_coll_list=collection["android_property_names"])
|
||||
|
||||
def parse_stix2(self, file_path: str) -> None:
|
||||
"""Extract indicators from a STIX2 file.
|
||||
|
||||
|
@ -519,3 +525,23 @@ class Indicators:
|
|||
return ioc
|
||||
|
||||
return None
|
||||
|
||||
def check_android_property_name(self, property_name: str) -> Optional[dict]:
|
||||
"""Check the android property name against the list of indicators.
|
||||
|
||||
:param property_name: Name of the Android property
|
||||
:type property_name: str
|
||||
:returns: Indicator details if matched, otherwise None
|
||||
|
||||
"""
|
||||
if property_name is None:
|
||||
return None
|
||||
|
||||
for ioc in self.get_iocs("android_property_names"):
|
||||
if property_name.lower() == ioc["value"].lower():
|
||||
self.log.warning("Found a known suspicious Android property \"%s\" "
|
||||
"matching indicators from \"%s\"", property_name,
|
||||
ioc["name"])
|
||||
return ioc
|
||||
|
||||
return None
|
||||
|
|
|
@ -7,6 +7,7 @@ import logging
|
|||
import os
|
||||
|
||||
from mvt.android.modules.androidqf.getprop import Getprop
|
||||
from mvt.common.indicators import Indicators
|
||||
from mvt.common.module import run_module
|
||||
|
||||
from ..utils import get_artifact_folder
|
||||
|
@ -18,5 +19,18 @@ class TestAndroidqfGetpropAnalysis:
|
|||
m = Getprop(target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging)
|
||||
run_module(m)
|
||||
assert len(m.results) == 10
|
||||
assert m.results[0]["name"] == "dalvik.vm.appimageformat"
|
||||
assert m.results[0]["value"] == "lz4"
|
||||
assert len(m.timeline) == 0
|
||||
assert len(m.detected) == 0
|
||||
|
||||
def test_androidqf_getprop_detection(self, indicator_file):
|
||||
m = Getprop(target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging)
|
||||
ind = Indicators(log=logging.getLogger())
|
||||
ind.parse_stix2(indicator_file)
|
||||
ind.ioc_collections[0]["android_property_names"].append("dalvik.vm.heapmaxfree")
|
||||
m.indicators = ind
|
||||
run_module(m)
|
||||
assert len(m.results) == 10
|
||||
assert len(m.detected) == 1
|
||||
assert m.detected[0]["name"] == "dalvik.vm.heapmaxfree"
|
||||
|
|
|
@ -16,6 +16,7 @@ def generate_test_stix_file(file_path):
|
|||
processes = ["Launch"]
|
||||
emails = ["foobar@example.org"]
|
||||
filenames = ["/var/foobar/txt"]
|
||||
android_property = ["sys.foobar"]
|
||||
|
||||
res = []
|
||||
malware = Malware(name="TestMalware", is_family=False, description="")
|
||||
|
@ -40,6 +41,11 @@ def generate_test_stix_file(file_path):
|
|||
res.append(i)
|
||||
res.append(Relationship(i, "indicates", malware))
|
||||
|
||||
for p in android_property:
|
||||
i = Indicator(indicator_types=["malicious-activity"], pattern="[android-property:name='{}']".format(p), pattern_type="stix")
|
||||
res.append(i)
|
||||
res.append(Relationship(i, "indicates", malware))
|
||||
|
||||
bundle = Bundle(objects=res)
|
||||
with open(file_path, "w+", encoding="utf-8") as f:
|
||||
f.write(bundle.serialize(pretty=True))
|
||||
|
|
|
@ -14,11 +14,12 @@ class TestIndicators:
|
|||
def test_parse_stix2(self, indicator_file):
|
||||
ind = Indicators(log=logging)
|
||||
ind.load_indicators_files([indicator_file], load_default=False)
|
||||
assert ind.ioc_collections[0]["count"] == 4
|
||||
assert ind.ioc_collections[0]["count"] == 5
|
||||
assert len(ind.ioc_collections[0]["domains"]) == 1
|
||||
assert len(ind.ioc_collections[0]["emails"]) == 1
|
||||
assert len(ind.ioc_collections[0]["file_names"]) == 1
|
||||
assert len(ind.ioc_collections[0]["processes"]) == 1
|
||||
assert len(ind.ioc_collections[0]["android_property_names"]) == 1
|
||||
|
||||
def test_check_domain(self, indicator_file):
|
||||
ind = Indicators(log=logging)
|
||||
|
@ -26,8 +27,14 @@ class TestIndicators:
|
|||
assert ind.check_domain("https://www.example.org/foobar")
|
||||
assert ind.check_domain("http://example.org:8080/toto")
|
||||
|
||||
def test_check_android_property(self, indicator_file):
|
||||
ind = Indicators(log=logging)
|
||||
ind.load_indicators_files([indicator_file], load_default=False)
|
||||
assert ind.check_android_property_name("sys.foobar")
|
||||
assert ind.check_android_property_name("sys.soundsokay") is None
|
||||
|
||||
def test_env_stix(self, indicator_file):
|
||||
os.environ["MVT_STIX2"] = indicator_file
|
||||
ind = Indicators(log=logging)
|
||||
ind.load_indicators_files([], load_default=False)
|
||||
assert ind.total_ioc_count == 4
|
||||
assert ind.total_ioc_count == 5
|
||||
|
|
Loading…
Reference in New Issue
Block a user