76 lines
2.9 KiB
Python
76 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Isolation checks for a malware-analysis victim VM.
|
|
|
|
Parses a VMware ``.vmx`` (or VirtualBox ``VBoxManage showvminfo`` text) and flags settings
|
|
that would let a sample reach the real internet or escape to the host: bridged/NAT
|
|
adapters, enabled shared folders, and clipboard/drag-and-drop sharing.
|
|
|
|
Safe to run on the host — it only reads configuration text, never a sample.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# vmx key -> (regex of unsafe value, human explanation)
|
|
_UNSAFE_VMX = {
|
|
"ethernet*.connectiontype": (re.compile(r"\b(nat|bridged)\b", re.I),
|
|
"adapter uses NAT/bridged; use host-only/internal"),
|
|
"isolation.tools.copy.disable": (re.compile(r"false", re.I),
|
|
"clipboard copy from host is enabled"),
|
|
"isolation.tools.paste.disable": (re.compile(r"false", re.I),
|
|
"clipboard paste to host is enabled"),
|
|
"isolation.tools.dnd.disable": (re.compile(r"false", re.I),
|
|
"drag-and-drop is enabled"),
|
|
"sharedfolder.maxnum": (re.compile(r"[1-9]"),
|
|
"shared folders are configured"),
|
|
}
|
|
|
|
|
|
def parse_vmx(text: str) -> dict[str, str]:
|
|
config: dict[str, str] = {}
|
|
for line in text.splitlines():
|
|
m = re.match(r'\s*([\w.*]+)\s*=\s*"?([^"]*)"?\s*$', line)
|
|
if m:
|
|
config[m.group(1).lower()] = m.group(2)
|
|
return config
|
|
|
|
|
|
def check_config(config: dict[str, str]) -> list[str]:
|
|
"""Return a list of isolation problems found in the parsed config."""
|
|
problems: list[str] = []
|
|
for key, value in config.items():
|
|
for pattern, (bad, why) in _UNSAFE_VMX.items():
|
|
# support wildcard keys like ethernet0.connectiontype
|
|
key_re = "^" + re.escape(pattern).replace(r"\*", r"\d+") + "$"
|
|
if re.match(key_re, key) and bad.search(value):
|
|
problems.append(f"{key} = {value!r}: {why}")
|
|
has_adapter = any(k.endswith(".connectiontype") for k in config)
|
|
if not has_adapter:
|
|
problems.append("no network adapter found; confirm host-only is intended")
|
|
return problems
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(description="Check victim VM isolation settings")
|
|
parser.add_argument("--check-config", required=True, metavar="VMX",
|
|
help="path to a .vmx file")
|
|
args = parser.parse_args(argv)
|
|
|
|
text = Path(args.check_config).read_text(encoding="utf-8", errors="replace")
|
|
problems = check_config(parse_vmx(text))
|
|
|
|
if problems:
|
|
print("ISOLATION PROBLEMS:")
|
|
for p in problems:
|
|
print(f" - {p}")
|
|
return 1
|
|
print("OK: no unsafe networking or host-sharing settings detected.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|