mirror of
https://github.com/mvt-project/mvt.git
synced 2024-07-01 08:29:03 +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
|
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:
|
def run(self) -> None:
|
||||||
self._adb_connect()
|
self._adb_connect()
|
||||||
output = self._adb_command("getprop")
|
output = self._adb_command("getprop")
|
||||||
|
@ -38,13 +48,14 @@ class Getprop(AndroidExtraction):
|
||||||
self.results = parse_getprop(output)
|
self.results = parse_getprop(output)
|
||||||
|
|
||||||
# Alert if phone is outdated.
|
# Alert if phone is outdated.
|
||||||
security_patch = self.results.get("ro.build.version.security_patch", "")
|
for entry in self.results:
|
||||||
if security_patch:
|
if entry.get("name", "") != "ro.build.version.security_patch":
|
||||||
patch_date = datetime.strptime(security_patch, "%Y-%m-%d")
|
continue
|
||||||
|
patch_date = datetime.strptime(entry["value"], "%Y-%m-%d")
|
||||||
if (datetime.now() - patch_date) > timedelta(days=6*30):
|
if (datetime.now() - patch_date) > timedelta(days=6*30):
|
||||||
self.log.warning("This phone has not received security updates "
|
self.log.warning("This phone has not received security updates "
|
||||||
"for more than six months (last update: %s)",
|
"for more than six months (last update: %s)",
|
||||||
security_patch)
|
entry["value"])
|
||||||
|
|
||||||
self.log.info("Extracted %d Android system properties",
|
self.log.info("Extracted %d Android system properties",
|
||||||
len(self.results))
|
len(self.results))
|
||||||
|
|
|
@ -7,7 +7,7 @@ import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from mvt.android.parsers import getprop
|
from mvt.android.parsers.getprop import parse_getprop
|
||||||
|
|
||||||
from .base import AndroidQFModule
|
from .base import AndroidQFModule
|
||||||
|
|
||||||
|
@ -41,7 +41,17 @@ class Getprop(AndroidQFModule):
|
||||||
super().__init__(file_path=file_path, target_path=target_path,
|
super().__init__(file_path=file_path, target_path=target_path,
|
||||||
results_path=results_path, fast_mode=fast_mode,
|
results_path=results_path, fast_mode=fast_mode,
|
||||||
log=log, results=results)
|
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:
|
def run(self) -> None:
|
||||||
getprop_files = self._get_files_by_pattern("*/getprop.txt")
|
getprop_files = self._get_files_by_pattern("*/getprop.txt")
|
||||||
|
@ -52,15 +62,15 @@ class Getprop(AndroidQFModule):
|
||||||
with open(getprop_files[0]) as f:
|
with open(getprop_files[0]) as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
|
|
||||||
self.results = getprop.parse_getprop(data)
|
self.results = parse_getprop(data)
|
||||||
for entry in self.results:
|
for entry in self.results:
|
||||||
if entry in INTERESTING_PROPERTIES:
|
if entry["name"] in INTERESTING_PROPERTIES:
|
||||||
self.log.info("%s: %s", entry, self.results[entry])
|
self.log.info("%s: %s", entry["name"], entry["value"])
|
||||||
if entry == "ro.build.version.security_patch":
|
if entry["name"] == "ro.build.version.security_patch":
|
||||||
last_patch = datetime.strptime(self.results[entry], "%Y-%m-%d")
|
last_patch = datetime.strptime(entry["value"], "%Y-%m-%d")
|
||||||
if (datetime.now() - last_patch) > timedelta(days=6*31):
|
if (datetime.now() - last_patch) > timedelta(days=6*31):
|
||||||
self.log.warning("This phone has not received security "
|
self.log.warning("This phone has not received security "
|
||||||
"updates for more than six months "
|
"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))
|
self.log.info("Extracted a total of %d properties", len(self.results))
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
|
||||||
def parse_getprop(output: str) -> dict:
|
def parse_getprop(output: str) -> List[Dict[str, str]]:
|
||||||
results = {}
|
results = []
|
||||||
rxp = re.compile(r"\[(.+?)\]: \[(.+?)\]")
|
rxp = re.compile(r"\[(.+?)\]: \[(.+?)\]")
|
||||||
|
|
||||||
for line in output.splitlines():
|
for line in output.splitlines():
|
||||||
|
@ -19,8 +20,10 @@ def parse_getprop(output: str) -> dict:
|
||||||
if not matches or len(matches[0]) != 2:
|
if not matches or len(matches[0]) != 2:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
key = matches[0][0]
|
entry = {
|
||||||
value = matches[0][1]
|
"name": matches[0][0],
|
||||||
results[key] = value
|
"value": matches[0][1]
|
||||||
|
}
|
||||||
|
results.append(entry)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -72,6 +72,7 @@ class Indicators:
|
||||||
"files_sha256": [],
|
"files_sha256": [],
|
||||||
"app_ids": [],
|
"app_ids": [],
|
||||||
"ios_profile_ids": [],
|
"ios_profile_ids": [],
|
||||||
|
"android_property_names": [],
|
||||||
"count": 0,
|
"count": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +122,11 @@ class Indicators:
|
||||||
ioc_coll=collection,
|
ioc_coll=collection,
|
||||||
ioc_coll_list=collection["ios_profile_ids"])
|
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:
|
def parse_stix2(self, file_path: str) -> None:
|
||||||
"""Extract indicators from a STIX2 file.
|
"""Extract indicators from a STIX2 file.
|
||||||
|
|
||||||
|
@ -519,3 +525,23 @@ class Indicators:
|
||||||
return ioc
|
return ioc
|
||||||
|
|
||||||
return None
|
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
|
import os
|
||||||
|
|
||||||
from mvt.android.modules.androidqf.getprop import Getprop
|
from mvt.android.modules.androidqf.getprop import Getprop
|
||||||
|
from mvt.common.indicators import Indicators
|
||||||
from mvt.common.module import run_module
|
from mvt.common.module import run_module
|
||||||
|
|
||||||
from ..utils import get_artifact_folder
|
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)
|
m = Getprop(target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging)
|
||||||
run_module(m)
|
run_module(m)
|
||||||
assert len(m.results) == 10
|
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.timeline) == 0
|
||||||
assert len(m.detected) == 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"]
|
processes = ["Launch"]
|
||||||
emails = ["foobar@example.org"]
|
emails = ["foobar@example.org"]
|
||||||
filenames = ["/var/foobar/txt"]
|
filenames = ["/var/foobar/txt"]
|
||||||
|
android_property = ["sys.foobar"]
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
malware = Malware(name="TestMalware", is_family=False, description="")
|
malware = Malware(name="TestMalware", is_family=False, description="")
|
||||||
|
@ -40,6 +41,11 @@ def generate_test_stix_file(file_path):
|
||||||
res.append(i)
|
res.append(i)
|
||||||
res.append(Relationship(i, "indicates", malware))
|
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)
|
bundle = Bundle(objects=res)
|
||||||
with open(file_path, "w+", encoding="utf-8") as f:
|
with open(file_path, "w+", encoding="utf-8") as f:
|
||||||
f.write(bundle.serialize(pretty=True))
|
f.write(bundle.serialize(pretty=True))
|
||||||
|
|
|
@ -14,11 +14,12 @@ class TestIndicators:
|
||||||
def test_parse_stix2(self, indicator_file):
|
def test_parse_stix2(self, indicator_file):
|
||||||
ind = Indicators(log=logging)
|
ind = Indicators(log=logging)
|
||||||
ind.load_indicators_files([indicator_file], load_default=False)
|
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]["domains"]) == 1
|
||||||
assert len(ind.ioc_collections[0]["emails"]) == 1
|
assert len(ind.ioc_collections[0]["emails"]) == 1
|
||||||
assert len(ind.ioc_collections[0]["file_names"]) == 1
|
assert len(ind.ioc_collections[0]["file_names"]) == 1
|
||||||
assert len(ind.ioc_collections[0]["processes"]) == 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):
|
def test_check_domain(self, indicator_file):
|
||||||
ind = Indicators(log=logging)
|
ind = Indicators(log=logging)
|
||||||
|
@ -26,8 +27,14 @@ class TestIndicators:
|
||||||
assert ind.check_domain("https://www.example.org/foobar")
|
assert ind.check_domain("https://www.example.org/foobar")
|
||||||
assert ind.check_domain("http://example.org:8080/toto")
|
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):
|
def test_env_stix(self, indicator_file):
|
||||||
os.environ["MVT_STIX2"] = indicator_file
|
os.environ["MVT_STIX2"] = indicator_file
|
||||||
ind = Indicators(log=logging)
|
ind = Indicators(log=logging)
|
||||||
ind.load_indicators_files([], load_default=False)
|
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