mirror of
https://github.com/mvt-project/mvt.git
synced 2024-06-17 10:04:05 +00:00
Compare commits
5 Commits
5e8bc33c60
...
e2aa5a6001
Author | SHA1 | Date | |
---|---|---|---|
|
e2aa5a6001 | ||
|
5826e6b11c | ||
|
54c5d549af | ||
|
dded863e58 | ||
|
529823c855 |
|
@ -48,7 +48,7 @@ ideviceinfo
|
||||||
This should show many details on the connected iOS device. If you are connecting the device to your laptop for the first time, it will require to unlock and enter the PIN code on the mobile device. If it complains that no device is connected and the mobile device is indeed plugged in through the USB cable, you might need to do this first, although typically the pairing is automatically done when connecting the device:
|
This should show many details on the connected iOS device. If you are connecting the device to your laptop for the first time, it will require to unlock and enter the PIN code on the mobile device. If it complains that no device is connected and the mobile device is indeed plugged in through the USB cable, you might need to do this first, although typically the pairing is automatically done when connecting the device:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo usbmuxd -f -d
|
sudo usbmuxd -f -v
|
||||||
idevicepair pair
|
idevicepair pair
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
203
mvt/android/artifacts/dumpsys_packages.py
Normal file
203
mvt/android/artifacts/dumpsys_packages.py
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
# 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 re
|
||||||
|
from typing import Any, Dict, List, Union
|
||||||
|
|
||||||
|
from mvt.android.utils import ROOT_PACKAGES
|
||||||
|
|
||||||
|
from .artifact import AndroidArtifact
|
||||||
|
|
||||||
|
|
||||||
|
class DumpsysPackagesArtifact(AndroidArtifact):
|
||||||
|
def check_indicators(self) -> None:
|
||||||
|
for result in self.results:
|
||||||
|
if result["package_name"] in ROOT_PACKAGES:
|
||||||
|
self.log.warning(
|
||||||
|
"Found an installed package related to "
|
||||||
|
'rooting/jailbreaking: "%s"',
|
||||||
|
result["package_name"],
|
||||||
|
)
|
||||||
|
self.detected.append(result)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not self.indicators:
|
||||||
|
continue
|
||||||
|
|
||||||
|
ioc = self.indicators.check_app_id(result.get("package_name", ""))
|
||||||
|
if ioc:
|
||||||
|
result["matched_indicator"] = ioc
|
||||||
|
self.detected.append(result)
|
||||||
|
|
||||||
|
def serialize(self, record: dict) -> Union[dict, list]:
|
||||||
|
records = []
|
||||||
|
|
||||||
|
timestamps = [
|
||||||
|
{"event": "package_install", "timestamp": record["timestamp"]},
|
||||||
|
{
|
||||||
|
"event": "package_first_install",
|
||||||
|
"timestamp": record["first_install_time"],
|
||||||
|
},
|
||||||
|
{"event": "package_last_update", "timestamp": record["last_update_time"]},
|
||||||
|
]
|
||||||
|
|
||||||
|
for timestamp in timestamps:
|
||||||
|
records.append(
|
||||||
|
{
|
||||||
|
"timestamp": timestamp["timestamp"],
|
||||||
|
"module": self.__class__.__name__,
|
||||||
|
"event": timestamp["event"],
|
||||||
|
"data": f"Install or update of package {record['package_name']}",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_dumpsys_package_for_details(output: str) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Parse one entry of a dumpsys package information
|
||||||
|
"""
|
||||||
|
details = {
|
||||||
|
"uid": "",
|
||||||
|
"version_name": "",
|
||||||
|
"version_code": "",
|
||||||
|
"timestamp": "",
|
||||||
|
"first_install_time": "",
|
||||||
|
"last_update_time": "",
|
||||||
|
"permissions": [],
|
||||||
|
"requested_permissions": [],
|
||||||
|
}
|
||||||
|
in_install_permissions = False
|
||||||
|
in_runtime_permissions = False
|
||||||
|
in_declared_permissions = False
|
||||||
|
in_requested_permissions = True
|
||||||
|
for line in output.splitlines():
|
||||||
|
if in_install_permissions:
|
||||||
|
if line.startswith(" " * 4) and not line.startswith(" " * 6):
|
||||||
|
in_install_permissions = False
|
||||||
|
else:
|
||||||
|
lineinfo = line.strip().split(":")
|
||||||
|
permission = lineinfo[0]
|
||||||
|
granted = None
|
||||||
|
if "granted=" in lineinfo[1]:
|
||||||
|
granted = "granted=true" in lineinfo[1]
|
||||||
|
|
||||||
|
details["permissions"].append(
|
||||||
|
{"name": permission, "granted": granted, "type": "install"}
|
||||||
|
)
|
||||||
|
if in_runtime_permissions:
|
||||||
|
if not line.startswith(" " * 8):
|
||||||
|
in_runtime_permissions = False
|
||||||
|
else:
|
||||||
|
lineinfo = line.strip().split(":")
|
||||||
|
permission = lineinfo[0]
|
||||||
|
granted = None
|
||||||
|
if "granted=" in lineinfo[1]:
|
||||||
|
granted = "granted=true" in lineinfo[1]
|
||||||
|
|
||||||
|
details["permissions"].append(
|
||||||
|
{"name": permission, "granted": granted, "type": "runtime"}
|
||||||
|
)
|
||||||
|
if in_declared_permissions:
|
||||||
|
if not line.startswith(" " * 6):
|
||||||
|
in_declared_permissions = False
|
||||||
|
else:
|
||||||
|
permission = line.strip().split(":")[0]
|
||||||
|
details["permissions"].append(
|
||||||
|
{"name": permission, "type": "declared"}
|
||||||
|
)
|
||||||
|
if in_requested_permissions:
|
||||||
|
if not line.startswith(" " * 6):
|
||||||
|
in_requested_permissions = False
|
||||||
|
else:
|
||||||
|
details["requested_permissions"].append(line.strip())
|
||||||
|
if line.strip().startswith("userId="):
|
||||||
|
details["uid"] = line.split("=")[1].strip()
|
||||||
|
elif line.strip().startswith("versionName="):
|
||||||
|
details["version_name"] = line.split("=")[1].strip()
|
||||||
|
elif line.strip().startswith("versionCode="):
|
||||||
|
details["version_code"] = line.split("=", 1)[1].strip()
|
||||||
|
elif line.strip().startswith("timeStamp="):
|
||||||
|
details["timestamp"] = line.split("=")[1].strip()
|
||||||
|
elif line.strip().startswith("firstInstallTime="):
|
||||||
|
details["first_install_time"] = line.split("=")[1].strip()
|
||||||
|
elif line.strip().startswith("lastUpdateTime="):
|
||||||
|
details["last_update_time"] = line.split("=")[1].strip()
|
||||||
|
elif line.strip() == "install permissions:":
|
||||||
|
in_install_permissions = True
|
||||||
|
elif line.strip() == "runtime permissions:":
|
||||||
|
in_runtime_permissions = True
|
||||||
|
elif line.strip() == "declared permissions:":
|
||||||
|
in_declared_permissions = True
|
||||||
|
elif line.strip() == "requested permissions:":
|
||||||
|
in_requested_permissions = True
|
||||||
|
|
||||||
|
return details
|
||||||
|
|
||||||
|
def parse_dumpsys_packages(self, output: str) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Parse the dumpsys package service data
|
||||||
|
"""
|
||||||
|
pkg_rxp = re.compile(r" Package \[(.+?)\].*")
|
||||||
|
|
||||||
|
results = []
|
||||||
|
package_name = None
|
||||||
|
package = {}
|
||||||
|
lines = []
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line.startswith(" Package ["):
|
||||||
|
if len(lines) > 0:
|
||||||
|
details = self.parse_dumpsys_package_for_details("\n".join(lines))
|
||||||
|
package.update(details)
|
||||||
|
results.append(package)
|
||||||
|
lines = []
|
||||||
|
package = {}
|
||||||
|
|
||||||
|
matches = pkg_rxp.findall(line)
|
||||||
|
if not matches:
|
||||||
|
continue
|
||||||
|
|
||||||
|
package_name = matches[0]
|
||||||
|
package["package_name"] = package_name
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not package_name:
|
||||||
|
continue
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
if len(lines) > 0:
|
||||||
|
details = self.parse_dumpsys_package_for_details("\n".join(lines))
|
||||||
|
package.update(details)
|
||||||
|
results.append(package)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def parse(self, content: str):
|
||||||
|
"""
|
||||||
|
Parse the Dumpsys Package section for activities
|
||||||
|
Adds results to self.results
|
||||||
|
|
||||||
|
:param content: content of the package section (string)
|
||||||
|
"""
|
||||||
|
self.results = []
|
||||||
|
package = []
|
||||||
|
|
||||||
|
in_package_list = False
|
||||||
|
for line in content.split("\n"):
|
||||||
|
if line.startswith("Packages:"):
|
||||||
|
in_package_list = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_package_list:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.strip() == "":
|
||||||
|
break
|
||||||
|
|
||||||
|
package.append(line)
|
||||||
|
|
||||||
|
self.results = self.parse_dumpsys_packages("\n".join(package))
|
|
@ -4,86 +4,25 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.progress import track
|
from rich.progress import track
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
from mvt.android.parsers.dumpsys import parse_dumpsys_package_for_details
|
from mvt.android.artifacts.dumpsys_packages import DumpsysPackagesArtifact
|
||||||
|
from mvt.android.utils import (
|
||||||
|
DANGEROUS_PERMISSIONS,
|
||||||
|
DANGEROUS_PERMISSIONS_THRESHOLD,
|
||||||
|
ROOT_PACKAGES,
|
||||||
|
SECURITY_PACKAGES,
|
||||||
|
SYSTEM_UPDATE_PACKAGES,
|
||||||
|
)
|
||||||
from mvt.common.virustotal import VTNoKey, VTQuotaExceeded, virustotal_lookup
|
from mvt.common.virustotal import VTNoKey, VTQuotaExceeded, virustotal_lookup
|
||||||
|
|
||||||
from .base import AndroidExtraction
|
from .base import AndroidExtraction
|
||||||
|
|
||||||
DANGEROUS_PERMISSIONS_THRESHOLD = 10
|
|
||||||
DANGEROUS_PERMISSIONS = [
|
|
||||||
"android.permission.ACCESS_COARSE_LOCATION",
|
|
||||||
"android.permission.ACCESS_FINE_LOCATION",
|
|
||||||
"android.permission.AUTHENTICATE_ACCOUNTS",
|
|
||||||
"android.permission.CAMERA",
|
|
||||||
"android.permission.DISABLE_KEYGUARD",
|
|
||||||
"android.permission.PROCESS_OUTGOING_CALLS",
|
|
||||||
"android.permission.READ_CALENDAR",
|
|
||||||
"android.permission.READ_CALL_LOG",
|
|
||||||
"android.permission.READ_CONTACTS",
|
|
||||||
"android.permission.READ_PHONE_STATE",
|
|
||||||
"android.permission.READ_SMS",
|
|
||||||
"android.permission.RECEIVE_MMS",
|
|
||||||
"android.permission.RECEIVE_SMS",
|
|
||||||
"android.permission.RECEIVE_WAP_PUSH",
|
|
||||||
"android.permission.RECORD_AUDIO",
|
|
||||||
"android.permission.SEND_SMS",
|
|
||||||
"android.permission.SYSTEM_ALERT_WINDOW",
|
|
||||||
"android.permission.USE_CREDENTIALS",
|
|
||||||
"android.permission.USE_SIP",
|
|
||||||
"com.android.browser.permission.READ_HISTORY_BOOKMARKS",
|
|
||||||
]
|
|
||||||
ROOT_PACKAGES: List[str] = [
|
|
||||||
"com.noshufou.android.su",
|
|
||||||
"com.noshufou.android.su.elite",
|
|
||||||
"eu.chainfire.supersu",
|
|
||||||
"com.koushikdutta.superuser",
|
|
||||||
"com.thirdparty.superuser",
|
|
||||||
"com.yellowes.su",
|
|
||||||
"com.koushikdutta.rommanager",
|
|
||||||
"com.koushikdutta.rommanager.license",
|
|
||||||
"com.dimonvideo.luckypatcher",
|
|
||||||
"com.chelpus.lackypatch",
|
|
||||||
"com.ramdroid.appquarantine",
|
|
||||||
"com.ramdroid.appquarantinepro",
|
|
||||||
"com.devadvance.rootcloak",
|
|
||||||
"com.devadvance.rootcloakplus",
|
|
||||||
"de.robv.android.xposed.installer",
|
|
||||||
"com.saurik.substrate",
|
|
||||||
"com.zachspong.temprootremovejb",
|
|
||||||
"com.amphoras.hidemyroot",
|
|
||||||
"com.amphoras.hidemyrootadfree",
|
|
||||||
"com.formyhm.hiderootPremium",
|
|
||||||
"com.formyhm.hideroot",
|
|
||||||
"me.phh.superuser",
|
|
||||||
"eu.chainfire.supersu.pro",
|
|
||||||
"com.kingouser.com",
|
|
||||||
"com.topjohnwu.magisk",
|
|
||||||
]
|
|
||||||
SECURITY_PACKAGES = [
|
|
||||||
"com.policydm",
|
|
||||||
"com.samsung.android.app.omcagent",
|
|
||||||
"com.samsung.android.securitylogagent",
|
|
||||||
"com.sec.android.soagent",
|
|
||||||
]
|
|
||||||
SYSTEM_UPDATE_PACKAGES = [
|
|
||||||
"com.android.updater",
|
|
||||||
"com.google.android.gms",
|
|
||||||
"com.huawei.android.hwouc",
|
|
||||||
"com.lge.lgdmsclient",
|
|
||||||
"com.motorola.ccc.ota",
|
|
||||||
"com.oneplus.opbackup",
|
|
||||||
"com.oppo.ota",
|
|
||||||
"com.transsion.systemupdate",
|
|
||||||
"com.wssyncmldm",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Packages(AndroidExtraction):
|
class Packages(AndroidExtraction):
|
||||||
"""This module extracts the list of installed packages."""
|
"""This module extracts the list of installed packages."""
|
||||||
|
@ -234,7 +173,9 @@ class Packages(AndroidExtraction):
|
||||||
if line.strip() == "Packages:":
|
if line.strip() == "Packages:":
|
||||||
in_packages = True
|
in_packages = True
|
||||||
|
|
||||||
return parse_dumpsys_package_for_details("\n".join(lines))
|
return DumpsysPackagesArtifact.parse_dumpsys_package_for_details(
|
||||||
|
"\n".join(lines)
|
||||||
|
)
|
||||||
|
|
||||||
def _get_files_for_package(self, package_name: str) -> list:
|
def _get_files_for_package(self, package_name: str) -> list:
|
||||||
command = f"pm path {package_name}"
|
command = f"pm path {package_name}"
|
||||||
|
|
|
@ -4,19 +4,18 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, List, Optional, Union
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
from mvt.android.artifacts.dumpsys_packages import DumpsysPackagesArtifact
|
||||||
from mvt.android.modules.adb.packages import (
|
from mvt.android.modules.adb.packages import (
|
||||||
DANGEROUS_PERMISSIONS,
|
DANGEROUS_PERMISSIONS,
|
||||||
DANGEROUS_PERMISSIONS_THRESHOLD,
|
DANGEROUS_PERMISSIONS_THRESHOLD,
|
||||||
ROOT_PACKAGES,
|
|
||||||
)
|
)
|
||||||
from mvt.android.parsers.dumpsys import parse_dumpsys_packages
|
|
||||||
|
|
||||||
from .base import AndroidQFModule
|
from .base import AndroidQFModule
|
||||||
|
|
||||||
|
|
||||||
class DumpsysPackages(AndroidQFModule):
|
class DumpsysPackages(DumpsysPackagesArtifact, AndroidQFModule):
|
||||||
"""This module analyse dumpsys packages"""
|
"""This module analyse dumpsys packages"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -37,70 +36,15 @@ class DumpsysPackages(AndroidQFModule):
|
||||||
results=results,
|
results=results,
|
||||||
)
|
)
|
||||||
|
|
||||||
def serialize(self, record: dict) -> Union[dict, list]:
|
|
||||||
entries = []
|
|
||||||
for entry in ["timestamp", "first_install_time", "last_update_time"]:
|
|
||||||
if entry in record:
|
|
||||||
entries.append(
|
|
||||||
{
|
|
||||||
"timestamp": record[entry],
|
|
||||||
"module": self.__class__.__name__,
|
|
||||||
"event": entry,
|
|
||||||
"data": f"Package {record['package_name']} "
|
|
||||||
f"({record['uid']})",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return entries
|
|
||||||
|
|
||||||
def check_indicators(self) -> None:
|
|
||||||
for result in self.results:
|
|
||||||
if result["package_name"] in ROOT_PACKAGES:
|
|
||||||
self.log.warning(
|
|
||||||
"Found an installed package related to "
|
|
||||||
'rooting/jailbreaking: "%s"',
|
|
||||||
result["package_name"],
|
|
||||||
)
|
|
||||||
self.detected.append(result)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not self.indicators:
|
|
||||||
continue
|
|
||||||
|
|
||||||
ioc = self.indicators.check_app_id(result.get("package_name", ""))
|
|
||||||
if ioc:
|
|
||||||
result["matched_indicator"] = ioc
|
|
||||||
self.detected.append(result)
|
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
|
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
|
||||||
if len(dumpsys_file) != 1:
|
if len(dumpsys_file) != 1:
|
||||||
self.log.info("Dumpsys file not found")
|
self.log.info("Dumpsys file not found")
|
||||||
return
|
return
|
||||||
|
|
||||||
data = self._get_file_content(dumpsys_file[0])
|
data = self._get_file_content(dumpsys_file[0]).decode("utf-8", errors="replace")
|
||||||
|
content = self.extract_dumpsys_section(data, "DUMP OF SERVICE package:")
|
||||||
package = []
|
self.parse(content)
|
||||||
in_service = False
|
|
||||||
in_package_list = False
|
|
||||||
for line in data.decode("utf-8").split("\n"):
|
|
||||||
if line.strip().startswith("DUMP OF SERVICE package:"):
|
|
||||||
in_service = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
if in_service and line.startswith("Packages:"):
|
|
||||||
in_package_list = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not in_service or not in_package_list:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if line.strip() == "":
|
|
||||||
break
|
|
||||||
|
|
||||||
package.append(line)
|
|
||||||
|
|
||||||
self.results = parse_dumpsys_packages("\n".join(package))
|
|
||||||
|
|
||||||
for result in self.results:
|
for result in self.results:
|
||||||
dangerous_permissions_count = 0
|
dangerous_permissions_count = 0
|
||||||
|
|
|
@ -4,19 +4,15 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, Union
|
from typing import Optional
|
||||||
|
|
||||||
from mvt.android.modules.adb.packages import (
|
from mvt.android.artifacts.dumpsys_packages import DumpsysPackagesArtifact
|
||||||
DANGEROUS_PERMISSIONS,
|
from mvt.android.utils import DANGEROUS_PERMISSIONS, DANGEROUS_PERMISSIONS_THRESHOLD
|
||||||
DANGEROUS_PERMISSIONS_THRESHOLD,
|
|
||||||
ROOT_PACKAGES,
|
|
||||||
)
|
|
||||||
from mvt.android.parsers.dumpsys import parse_dumpsys_packages
|
|
||||||
|
|
||||||
from .base import BugReportModule
|
from .base import BugReportModule
|
||||||
|
|
||||||
|
|
||||||
class Packages(BugReportModule):
|
class Packages(DumpsysPackagesArtifact, BugReportModule):
|
||||||
"""This module extracts details on receivers for risky activities."""
|
"""This module extracts details on receivers for risky activities."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -37,83 +33,18 @@ class Packages(BugReportModule):
|
||||||
results=results,
|
results=results,
|
||||||
)
|
)
|
||||||
|
|
||||||
def serialize(self, record: dict) -> Union[dict, list]:
|
|
||||||
records = []
|
|
||||||
|
|
||||||
timestamps = [
|
|
||||||
{"event": "package_install", "timestamp": record["timestamp"]},
|
|
||||||
{
|
|
||||||
"event": "package_first_install",
|
|
||||||
"timestamp": record["first_install_time"],
|
|
||||||
},
|
|
||||||
{"event": "package_last_update", "timestamp": record["last_update_time"]},
|
|
||||||
]
|
|
||||||
|
|
||||||
for timestamp in timestamps:
|
|
||||||
records.append(
|
|
||||||
{
|
|
||||||
"timestamp": timestamp["timestamp"],
|
|
||||||
"module": self.__class__.__name__,
|
|
||||||
"event": timestamp["event"],
|
|
||||||
"data": f"Install or update of package {record['package_name']}",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return records
|
|
||||||
|
|
||||||
def check_indicators(self) -> None:
|
|
||||||
for result in self.results:
|
|
||||||
if result["package_name"] in ROOT_PACKAGES:
|
|
||||||
self.log.warning(
|
|
||||||
"Found an installed package related to "
|
|
||||||
'rooting/jailbreaking: "%s"',
|
|
||||||
result["package_name"],
|
|
||||||
)
|
|
||||||
self.detected.append(result)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not self.indicators:
|
|
||||||
continue
|
|
||||||
|
|
||||||
ioc = self.indicators.check_app_id(result.get("package_name"))
|
|
||||||
if ioc:
|
|
||||||
result["matched_indicator"] = ioc
|
|
||||||
self.detected.append(result)
|
|
||||||
continue
|
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
content = self._get_dumpstate_file()
|
data = self._get_dumpstate_file()
|
||||||
if not content:
|
if not data:
|
||||||
self.log.error(
|
self.log.error(
|
||||||
"Unable to find dumpstate file. "
|
"Unable to find dumpstate file. "
|
||||||
"Did you provide a valid bug report archive?"
|
"Did you provide a valid bug report archive?"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
in_package = False
|
data = data.decode("utf-8", errors="replace")
|
||||||
in_packages_list = False
|
content = self.extract_dumpsys_section(data, "DUMP OF SERVICE package:")
|
||||||
lines = []
|
self.parse(content)
|
||||||
for line in content.decode(errors="ignore").splitlines():
|
|
||||||
if line.strip() == "DUMP OF SERVICE package:":
|
|
||||||
in_package = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not in_package:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if line.strip() == "Packages:":
|
|
||||||
in_packages_list = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not in_packages_list:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if line.strip() == "":
|
|
||||||
break
|
|
||||||
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
self.results = parse_dumpsys_packages("\n".join(lines))
|
|
||||||
|
|
||||||
for result in self.results:
|
for result in self.results:
|
||||||
dangerous_permissions_count = 0
|
dangerous_permissions_count = 0
|
||||||
|
|
|
@ -1,131 +0,0 @@
|
||||||
# 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 re
|
|
||||||
from typing import Any, Dict, List
|
|
||||||
|
|
||||||
|
|
||||||
def parse_dumpsys_package_for_details(output: str) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Parse one entry of a dumpsys package information
|
|
||||||
"""
|
|
||||||
details = {
|
|
||||||
"uid": "",
|
|
||||||
"version_name": "",
|
|
||||||
"version_code": "",
|
|
||||||
"timestamp": "",
|
|
||||||
"first_install_time": "",
|
|
||||||
"last_update_time": "",
|
|
||||||
"permissions": [],
|
|
||||||
"requested_permissions": [],
|
|
||||||
}
|
|
||||||
|
|
||||||
in_install_permissions = False
|
|
||||||
in_runtime_permissions = False
|
|
||||||
in_declared_permissions = False
|
|
||||||
in_requested_permissions = True
|
|
||||||
for line in output.splitlines():
|
|
||||||
if in_install_permissions:
|
|
||||||
if line.startswith(" " * 4) and not line.startswith(" " * 6):
|
|
||||||
in_install_permissions = False
|
|
||||||
else:
|
|
||||||
lineinfo = line.strip().split(":")
|
|
||||||
permission = lineinfo[0]
|
|
||||||
granted = None
|
|
||||||
if "granted=" in lineinfo[1]:
|
|
||||||
granted = "granted=true" in lineinfo[1]
|
|
||||||
|
|
||||||
details["permissions"].append(
|
|
||||||
{"name": permission, "granted": granted, "type": "install"}
|
|
||||||
)
|
|
||||||
|
|
||||||
if in_runtime_permissions:
|
|
||||||
if not line.startswith(" " * 8):
|
|
||||||
in_runtime_permissions = False
|
|
||||||
else:
|
|
||||||
lineinfo = line.strip().split(":")
|
|
||||||
permission = lineinfo[0]
|
|
||||||
granted = None
|
|
||||||
if "granted=" in lineinfo[1]:
|
|
||||||
granted = "granted=true" in lineinfo[1]
|
|
||||||
|
|
||||||
details["permissions"].append(
|
|
||||||
{"name": permission, "granted": granted, "type": "runtime"}
|
|
||||||
)
|
|
||||||
|
|
||||||
if in_declared_permissions:
|
|
||||||
if not line.startswith(" " * 6):
|
|
||||||
in_declared_permissions = False
|
|
||||||
else:
|
|
||||||
permission = line.strip().split(":")[0]
|
|
||||||
details["permissions"].append({"name": permission, "type": "declared"})
|
|
||||||
if in_requested_permissions:
|
|
||||||
if not line.startswith(" " * 6):
|
|
||||||
in_requested_permissions = False
|
|
||||||
else:
|
|
||||||
details["requested_permissions"].append(line.strip())
|
|
||||||
|
|
||||||
if line.strip().startswith("userId="):
|
|
||||||
details["uid"] = line.split("=")[1].strip()
|
|
||||||
elif line.strip().startswith("versionName="):
|
|
||||||
details["version_name"] = line.split("=")[1].strip()
|
|
||||||
elif line.strip().startswith("versionCode="):
|
|
||||||
details["version_code"] = line.split("=", 1)[1].strip()
|
|
||||||
elif line.strip().startswith("timeStamp="):
|
|
||||||
details["timestamp"] = line.split("=")[1].strip()
|
|
||||||
elif line.strip().startswith("firstInstallTime="):
|
|
||||||
details["first_install_time"] = line.split("=")[1].strip()
|
|
||||||
elif line.strip().startswith("lastUpdateTime="):
|
|
||||||
details["last_update_time"] = line.split("=")[1].strip()
|
|
||||||
elif line.strip() == "install permissions:":
|
|
||||||
in_install_permissions = True
|
|
||||||
elif line.strip() == "runtime permissions:":
|
|
||||||
in_runtime_permissions = True
|
|
||||||
elif line.strip() == "declared permissions:":
|
|
||||||
in_declared_permissions = True
|
|
||||||
elif line.strip() == "requested permissions:":
|
|
||||||
in_requested_permissions = True
|
|
||||||
|
|
||||||
return details
|
|
||||||
|
|
||||||
|
|
||||||
def parse_dumpsys_packages(output: str) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Parse the dumpsys package service data
|
|
||||||
"""
|
|
||||||
pkg_rxp = re.compile(r" Package \[(.+?)\].*")
|
|
||||||
|
|
||||||
results = []
|
|
||||||
package_name = None
|
|
||||||
package = {}
|
|
||||||
lines = []
|
|
||||||
for line in output.splitlines():
|
|
||||||
if line.startswith(" Package ["):
|
|
||||||
if len(lines) > 0:
|
|
||||||
details = parse_dumpsys_package_for_details("\n".join(lines))
|
|
||||||
package.update(details)
|
|
||||||
results.append(package)
|
|
||||||
lines = []
|
|
||||||
package = {}
|
|
||||||
|
|
||||||
matches = pkg_rxp.findall(line)
|
|
||||||
if not matches:
|
|
||||||
continue
|
|
||||||
|
|
||||||
package_name = matches[0]
|
|
||||||
package["package_name"] = package_name
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not package_name:
|
|
||||||
continue
|
|
||||||
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
if len(lines) > 0:
|
|
||||||
details = parse_dumpsys_package_for_details("\n".join(lines))
|
|
||||||
package.update(details)
|
|
||||||
results.append(package)
|
|
||||||
|
|
||||||
return results
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
def warn_android_patch_level(patch_level: str, log) -> bool:
|
def warn_android_patch_level(patch_level: str, log) -> bool:
|
||||||
|
@ -17,3 +18,76 @@ def warn_android_patch_level(patch_level: str, log) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
ROOT_PACKAGES: List[str] = [
|
||||||
|
"com.noshufou.android.su",
|
||||||
|
"com.noshufou.android.su.elite",
|
||||||
|
"eu.chainfire.supersu",
|
||||||
|
"com.koushikdutta.superuser",
|
||||||
|
"com.thirdparty.superuser",
|
||||||
|
"com.yellowes.su",
|
||||||
|
"com.koushikdutta.rommanager",
|
||||||
|
"com.koushikdutta.rommanager.license",
|
||||||
|
"com.dimonvideo.luckypatcher",
|
||||||
|
"com.chelpus.lackypatch",
|
||||||
|
"com.ramdroid.appquarantine",
|
||||||
|
"com.ramdroid.appquarantinepro",
|
||||||
|
"com.devadvance.rootcloak",
|
||||||
|
"com.devadvance.rootcloakplus",
|
||||||
|
"de.robv.android.xposed.installer",
|
||||||
|
"com.saurik.substrate",
|
||||||
|
"com.zachspong.temprootremovejb",
|
||||||
|
"com.amphoras.hidemyroot",
|
||||||
|
"com.amphoras.hidemyrootadfree",
|
||||||
|
"com.formyhm.hiderootPremium",
|
||||||
|
"com.formyhm.hideroot",
|
||||||
|
"me.phh.superuser",
|
||||||
|
"eu.chainfire.supersu.pro",
|
||||||
|
"com.kingouser.com",
|
||||||
|
"com.topjohnwu.magisk",
|
||||||
|
]
|
||||||
|
|
||||||
|
DANGEROUS_PERMISSIONS_THRESHOLD = 10
|
||||||
|
|
||||||
|
DANGEROUS_PERMISSIONS = [
|
||||||
|
"android.permission.ACCESS_COARSE_LOCATION",
|
||||||
|
"android.permission.ACCESS_FINE_LOCATION",
|
||||||
|
"android.permission.AUTHENTICATE_ACCOUNTS",
|
||||||
|
"android.permission.CAMERA",
|
||||||
|
"android.permission.DISABLE_KEYGUARD",
|
||||||
|
"android.permission.PROCESS_OUTGOING_CALLS",
|
||||||
|
"android.permission.READ_CALENDAR",
|
||||||
|
"android.permission.READ_CALL_LOG",
|
||||||
|
"android.permission.READ_CONTACTS",
|
||||||
|
"android.permission.READ_PHONE_STATE",
|
||||||
|
"android.permission.READ_SMS",
|
||||||
|
"android.permission.RECEIVE_MMS",
|
||||||
|
"android.permission.RECEIVE_SMS",
|
||||||
|
"android.permission.RECEIVE_WAP_PUSH",
|
||||||
|
"android.permission.RECORD_AUDIO",
|
||||||
|
"android.permission.SEND_SMS",
|
||||||
|
"android.permission.SYSTEM_ALERT_WINDOW",
|
||||||
|
"android.permission.USE_CREDENTIALS",
|
||||||
|
"android.permission.USE_SIP",
|
||||||
|
"com.android.browser.permission.READ_HISTORY_BOOKMARKS",
|
||||||
|
]
|
||||||
|
|
||||||
|
SECURITY_PACKAGES = [
|
||||||
|
"com.policydm",
|
||||||
|
"com.samsung.android.app.omcagent",
|
||||||
|
"com.samsung.android.securitylogagent",
|
||||||
|
"com.sec.android.soagent",
|
||||||
|
]
|
||||||
|
|
||||||
|
SYSTEM_UPDATE_PACKAGES = [
|
||||||
|
"com.android.updater",
|
||||||
|
"com.google.android.gms",
|
||||||
|
"com.huawei.android.hwouc",
|
||||||
|
"com.lge.lgdmsclient",
|
||||||
|
"com.motorola.ccc.ota",
|
||||||
|
"com.oneplus.opbackup",
|
||||||
|
"com.oppo.ota",
|
||||||
|
"com.transsion.systemupdate",
|
||||||
|
"com.wssyncmldm",
|
||||||
|
]
|
||||||
|
|
|
@ -1051,5 +1051,9 @@
|
||||||
{
|
{
|
||||||
"version": "17.4.1",
|
"version": "17.4.1",
|
||||||
"build": "21E236"
|
"build": "21E236"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "17.4.1",
|
||||||
|
"build": "21E237"
|
||||||
}
|
}
|
||||||
]
|
]
|
42
tests/android/test_artifact_dumpsys_packages.py
Normal file
42
tests/android/test_artifact_dumpsys_packages.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# 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.android.artifacts.dumpsys_packages import DumpsysPackagesArtifact
|
||||||
|
from mvt.common.indicators import Indicators
|
||||||
|
|
||||||
|
from ..utils import get_artifact
|
||||||
|
|
||||||
|
|
||||||
|
class TestDumpsysPackagesArtifact:
|
||||||
|
def test_parsing(self):
|
||||||
|
dpa = DumpsysPackagesArtifact()
|
||||||
|
file = get_artifact("android_data/dumpsys_packages.txt")
|
||||||
|
with open(file) as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
assert len(dpa.results) == 0
|
||||||
|
dpa.parse(data)
|
||||||
|
assert len(dpa.results) == 2
|
||||||
|
assert (
|
||||||
|
dpa.results[0]["package_name"]
|
||||||
|
== "com.samsung.android.provider.filterprovider"
|
||||||
|
)
|
||||||
|
assert dpa.results[0]["version_name"] == "5.0.07"
|
||||||
|
|
||||||
|
def test_ioc_check(self, indicator_file):
|
||||||
|
dpa = DumpsysPackagesArtifact()
|
||||||
|
file = get_artifact("android_data/dumpsys_packages.txt")
|
||||||
|
with open(file) as f:
|
||||||
|
data = f.read()
|
||||||
|
dpa.parse(data)
|
||||||
|
|
||||||
|
ind = Indicators(log=logging.getLogger())
|
||||||
|
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
|
||||||
|
dpa.check_indicators()
|
||||||
|
assert len(dpa.detected) == 1
|
|
@ -4,7 +4,6 @@
|
||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
from mvt.common.indicators import Indicators
|
from mvt.common.indicators import Indicators
|
||||||
from mvt.common.module import run_module
|
from mvt.common.module import run_module
|
||||||
from mvt.ios.modules.fs.filesystem import Filesystem
|
from mvt.ios.modules.fs.filesystem import Filesystem
|
||||||
|
|
Loading…
Reference in New Issue
Block a user