#!/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())