Added Getprop module and cleaned Files and Packages Android modules

This commit is contained in:
Nex
2022-01-27 12:50:37 +01:00
parent cce9159eda
commit 25c6c03075
6 changed files with 124 additions and 69 deletions
+3 -2
View File
@@ -13,12 +13,13 @@ from .dumpsys_receivers import DumpsysReceivers
from .files import Files from .files import Files
from .logcat import Logcat from .logcat import Logcat
from .packages import Packages from .packages import Packages
from .getprop import Getprop
from .processes import Processes from .processes import Processes
from .rootbinaries import RootBinaries from .root_binaries import RootBinaries
from .sms import SMS from .sms import SMS
from .whatsapp import Whatsapp from .whatsapp import Whatsapp
ADB_MODULES = [ChromeHistory, SMS, Whatsapp, Processes, ADB_MODULES = [ChromeHistory, SMS, Whatsapp, Processes, Getprop,
DumpsysAccessibility, DumpsysBatterystats, DumpsysProcstats, DumpsysAccessibility, DumpsysBatterystats, DumpsysProcstats,
DumpsysPackages, DumpsysReceivers, DumpsysFull, DumpsysPackages, DumpsysReceivers, DumpsysFull,
Packages, RootBinaries, Logcat, Files] Packages, RootBinaries, Logcat, Files]
+17 -39
View File
@@ -22,30 +22,16 @@ class Files(AndroidExtraction):
super().__init__(file_path=file_path, base_folder=base_folder, super().__init__(file_path=file_path, base_folder=base_folder,
output_folder=output_folder, fast_mode=fast_mode, output_folder=output_folder, fast_mode=fast_mode,
log=log, results=results) log=log, results=results)
self.full_find = None self.full_find = False
def find_path(self, file_path): def find_files(self, file_path):
"""Checks if Android system supports full find command output""" if self.full_find:
# Check find command params on first run
# Run find command with correct args and parse results.
# Check that full file printf options are suppported on first run.
if self.full_find is None:
output = self._adb_command("find '/' -maxdepth 1 -printf '%T@ %m %s %u %g %p\n' 2> /dev/null")
if not (output or output.strip().splitlines()):
# Full find command failed to generate output, fallback to basic file arguments
self.full_find = False
else:
self.full_find = True
found_files = []
if self.full_find is True:
# Run full file command and collect additonal file information.
output = self._adb_command(f"find '{file_path}' -printf '%T@ %m %s %u %g %p\n' 2> /dev/null") output = self._adb_command(f"find '{file_path}' -printf '%T@ %m %s %u %g %p\n' 2> /dev/null")
for file_line in output.splitlines(): for file_line in output.splitlines():
[unix_timestamp, mode, size, owner, group, full_path] = file_line.rstrip().split(" ", 5) [unix_timestamp, mode, size, owner, group, full_path] = file_line.rstrip().split(" ", 5)
mod_time = convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(int(float(unix_timestamp)))) mod_time = convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(int(float(unix_timestamp))))
found_files.append({ self.results.append({
"path": full_path, "path": full_path,
"modified_time": mod_time, "modified_time": mod_time,
"mode": mode, "mode": mode,
@@ -56,14 +42,9 @@ class Files(AndroidExtraction):
"group": group, "group": group,
}) })
else: else:
# Run a basic listing of file paths.
output = self._adb_command(f"find '{file_path}' 2> /dev/null") output = self._adb_command(f"find '{file_path}' 2> /dev/null")
for file_line in output.splitlines(): for file_line in output.splitlines():
found_files.append({ self.results.append({"path": file_line.rstrip()})
"path": file_line.rstrip()
})
return found_files
def serialize(self, record): def serialize(self, record):
if "modified_time" in record: if "modified_time" in record:
@@ -85,6 +66,7 @@ class Files(AndroidExtraction):
def check_indicators(self): def check_indicators(self):
"""Check file list for known suspicious files or suspicious properties""" """Check file list for known suspicious files or suspicious properties"""
self.check_suspicious() self.check_suspicious()
if not self.indicators: if not self.indicators:
return return
@@ -95,25 +77,21 @@ class Files(AndroidExtraction):
def run(self): def run(self):
self._adb_connect() self._adb_connect()
found_file_paths = []
DATA_PATHS = ["/data/local/tmp/", "/sdcard/", "/tmp/"] output = self._adb_command("find '/' -maxdepth 1 -printf '%T@ %m %s %u %g %p\n' 2> /dev/null")
for path in DATA_PATHS: if output or output.strip().splitlines():
file_info = self.find_path(path) self.full_find = True
found_file_paths.extend(file_info)
# Store results for data_path in ["/data/local/tmp/", "/sdcard/", "/tmp/"]:
self.results.extend(found_file_paths) self.find_files(data_path)
self.log.info("Found %s files in primary Android data directories.", len(found_file_paths))
self.log.info("Found %s files in primary Android data directories", len(self.results))
if self.fast_mode: if self.fast_mode:
self.log.info("Flag --fast was enabled: skipping full file listing") self.log.info("Flag --fast was enabled: skipping full file listing")
else: else:
self.log.info("Flag --fast was not enabled: processing full file listing. " self.log.info("Processing full file listing. This may take a while...")
"This may take a while...") self.find_files("/")
output = self.find_path("/") self.log.info("Found %s total files", len(self.results))
if output and self.output_folder:
self.results.extend(output)
log.info("List of visible files stored in files.json")
self._adb_disconnect() self._adb_disconnect()
+46
View File
@@ -0,0 +1,46 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021 The MVT Project 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 re
import logging
import os
from .base import AndroidExtraction
log = logging.getLogger(__name__)
class Getprop(AndroidExtraction):
"""This module extracts device properties from getprop command."""
def __init__(self, file_path=None, base_folder=None, output_folder=None,
serial=None, fast_mode=False, log=None, results=[]):
super().__init__(file_path=file_path, base_folder=base_folder,
output_folder=output_folder, fast_mode=fast_mode,
log=log, results=results)
self.results = {}
def run(self):
self._adb_connect()
rxp = re.compile("\\[(.+?)\\]: \\[(.+?)\\]")
out = self._adb_command("getprop")
for line in out.split("\n"):
line = line.strip()
if line == "":
continue
matches = re.findall(rxp, line)
if not matches or len(matches[0]) != 2:
continue
key = matches[0][0]
value = matches[0][1]
self.results[key] = value
self._adb_disconnect()
self.log.info("Extracted %d Android system properties", len(self.results))
+12 -12
View File
@@ -42,9 +42,6 @@ class Packages(AndroidExtraction):
return records return records
def check_indicators(self): def check_indicators(self):
if not self.indicators:
return
root_packages_path = os.path.join("..", "..", "data", "root_packages.txt") root_packages_path = os.path.join("..", "..", "data", "root_packages.txt")
root_packages_string = pkg_resources.resource_string(__name__, root_packages_path) root_packages_string = pkg_resources.resource_string(__name__, root_packages_path)
root_packages = root_packages_string.decode("utf-8").split("\n") root_packages = root_packages_string.decode("utf-8").split("\n")
@@ -55,16 +52,19 @@ class Packages(AndroidExtraction):
self.log.warning("Found an installed package related to rooting/jailbreaking: \"%s\"", self.log.warning("Found an installed package related to rooting/jailbreaking: \"%s\"",
result["package_name"]) result["package_name"])
self.detected.append(result) self.detected.append(result)
if result["package_name"] in self.indicators.ioc_app_ids: continue
self.log.warning("Found a malicious package name: \"%s\"",
result["package_name"]) ioc = self.indicators.check_app_id(result.get("package_name"))
if ioc:
result["matched_indicators"] = ioc
self.detected.append(result) self.detected.append(result)
for file in result["files"]: continue
if file["sha256"] in self.indicators.ioc_files_sha256:
self.log.warning("Found a malicious APK: \"%s\" %s", for package_file in result["files"]:
result["package_name"], ioc = self.indicators.check_file_hash(package_file["sha256"])
file["sha256"]) if ioc:
self.detected.append(result) result["matched_indicators"] = ioc
self.detected.append(result)
def _get_files_for_package(self, package_name): def _get_files_for_package(self, package_name):
output = self._adb_command(f"pm path {package_name}") output = self._adb_command(f"pm path {package_name}")
+46 -16
View File
@@ -173,8 +173,7 @@ class Indicators:
:param url: URL to match against domain indicators :param url: URL to match against domain indicators
:type url: str :type url: str
:returns: True if the URL matched an indicator, otherwise False :returns: Indicator details if matched, otherwise None
:rtype: bool
""" """
# TODO: If the IOC domain contains a subdomain, it is not currently # TODO: If the IOC domain contains a subdomain, it is not currently
@@ -246,8 +245,7 @@ class Indicators:
:param urls: List of URLs to check against domain indicators :param urls: List of URLs to check against domain indicators
:type urls: list :type urls: list
:returns: True if any URL matched an indicator, otherwise False :returns: Indicator details if matched, otherwise None
:rtype: bool
""" """
if not urls: if not urls:
@@ -264,8 +262,7 @@ class Indicators:
:param process: Process name to check against process indicators :param process: Process name to check against process indicators
:type process: str :type process: str
:returns: True if process matched an indicator, otherwise False :returns: Indicator details if matched, otherwise None
:rtype: bool
""" """
if not process: if not process:
@@ -290,8 +287,7 @@ class Indicators:
:param processes: List of processes to check against process indicators :param processes: List of processes to check against process indicators
:type processes: list :type processes: list
:returns: True if process matched an indicator, otherwise False :returns: Indicator details if matched, otherwise None
:rtype: bool
""" """
if not processes: if not processes:
@@ -307,8 +303,7 @@ class Indicators:
:param email: Email address to check against email indicators :param email: Email address to check against email indicators
:type email: str :type email: str
:returns: True if email address matched an indicator, otherwise False :returns: Indicator details if matched, otherwise None
:rtype: bool
""" """
if not email: if not email:
@@ -326,8 +321,7 @@ class Indicators:
:param file_name: File name to check against file :param file_name: File name to check against file
indicators indicators
:type file_name: str :type file_name: str
:returns: True if the file name matched an indicator, otherwise False :returns: Indicator details if matched, otherwise None
:rtype: bool
""" """
if not file_name: if not file_name:
@@ -345,8 +339,7 @@ class Indicators:
:param file_path: File path or file name to check against file :param file_path: File path or file name to check against file
indicators indicators
:type file_path: str :type file_path: str
:returns: True if the file path matched an indicator, otherwise False :returns: Indicator details if matched, otherwise None
:rtype: bool
""" """
if not file_path: if not file_path:
@@ -368,16 +361,53 @@ class Indicators:
:param profile_uuid: Profile UUID to check against configuration profile indicators :param profile_uuid: Profile UUID to check against configuration profile indicators
:type profile_uuid: str :type profile_uuid: str
:returns: True if the UUID in indicator list, otherwise False :returns: Indicator details if matched, otherwise None
:rtype: bool
""" """
if not profile_uuid:
return None
for ioc in self.get_iocs("ios_profile_ids"): for ioc in self.get_iocs("ios_profile_ids"):
if profile_uuid in ioc["value"]: if profile_uuid in ioc["value"]:
self.log.warning("Found a known suspicious profile ID \"%s\" matching indicators from \"%s\"", self.log.warning("Found a known suspicious profile ID \"%s\" matching indicators from \"%s\"",
profile_uuid, ioc["name"]) profile_uuid, ioc["name"])
return ioc return ioc
def check_file_hash(self, file_hash):
"""Check the provided SHA256 file hash against the list of indicators.
:param file_hash: SHA256 hash to check
:type file_hash: str
:returns: Indicator details if matched, otherwise None
"""
if not file_hash:
return None
for ioc in self.get_iocs("files_sha256"):
if file_hash.lower() == ioc["value"].lower():
self.log.warning("Found a known suspicious file with hash \"%s\" matching indicators from \"%s\"",
file_hash, ioc["name"])
return ioc
def check_app_id(self, app_id):
"""Check the provided app identifier (typically an Android package name)
against the list of indicators.
:param app_id: App ID to check against the list of indicators
:type app_id: str
:returns: Indicator details if matched, otherwise None
"""
if not app_id:
return None
for ioc in self.get_iocs("app_ids"):
if app_id.lower() == ioc["value"].lower():
self.log.warning("Found a known suspicious app with ID \"%s\" matching indicators from \"%s\"",
app_id, ioc["name"])
return ioc
def download_indicators_files(log): def download_indicators_files(log):
""" """