diff --git a/.env.example b/.env.example index 5bae447..ab7f840 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,9 @@ # ATOMIC_RED_TEAM_REPO= # Atomic tests directory (e.g. atomics/) # ATOMIC_TESTS_PATH= +# Platform (e.g. linux , default = windows) +# PLATFORM=linux + # --- Splunk Connection --- # Splunk host (hostname or IP) @@ -61,6 +64,13 @@ ATTACK_TIDS=T1059.001,T1087.001,T1003.001 # VM_PASSWORD= # Safe directory on VM for Atomic Red Team (e.g. C:\AtomicRedTeam) # VM_SAFE_DIR= +# VM SSH port (default 22, e.g. 2222 for VirtualBox NAT) +# VM_SSH_PORT=22 +# Path to SSH private key (optional; if not set, password auth is used) +# VM_SSH_KEY_PATH= +# Linux only: real NIC name of the VM (e.g. enp0s3). Overrides Atomic tests' +# default "interface" arg (often eth0/ens33) so packet-capture tests work. +# VM_INTERFACE= # --- Atomic Red Team Paths (Windows VM; optional) --- # Path to Invoke-AtomicRedTeam.psd1 on the VM @@ -69,6 +79,8 @@ ATTACK_TIDS=T1059.001,T1087.001,T1003.001 # ATOMIC_ATOMICS_PATH=C:\AtomicRedTeam\atomics # --- Proxmox (optional; for snapshot-based lab VMs) --- +# Set to false if not using Proxmox (e.g. VirtualBox, VMware) +# USE_PROXMOX=true # Proxmox host # PROXMOX_HOST= # Proxmox user (default root) diff --git a/.gitignore b/.gitignore index add0dc5..a1924ee 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,8 @@ data/repos/ __pycache__/ *.pyc # Logs -*.log \ No newline at end of file +*.log +# Local scratch/analysis scripts +_scratch/ +# PR draft docs (not part of the repo) +PR_MESSAGE.md diff --git a/add_splunk_macros.py b/add_splunk_macros.py new file mode 100644 index 0000000..502d872 --- /dev/null +++ b/add_splunk_macros.py @@ -0,0 +1,66 @@ +""" +Splunk ESCU Filter Macro Installer + +ESCU (Enterprise Security Content Updates) detection rules reference filter macros +(e.g. `linux_auditd_add_user_account_type_filter`) that must exist in Splunk's +macros.conf for the searches to run without errors. By default, these macros are +not defined, causing "macro not found" errors during detection verification. + +This script: +1. Scans all ESCU detection rules for `*_filter` macro references +2. Checks which ones are already defined in macros.conf +3. Adds missing macros with a passthrough definition (`search *`) + +Usage: + python add_splunk_macros.py + +Note: Requires Splunk to be installed locally. Update SPLUNK_MACROS_CONF path +if your Splunk installation is in a different location. Restart Splunk after running. +""" + +import re +import os +import glob + +DETECTIONS_PATH = os.path.join(os.path.dirname(__file__), "data", "repos", + "security_content", "detections", "endpoint") + +SPLUNK_MACROS_CONF = r"C:\Program Files\Splunk\etc\system\local\macros.conf" + +def find_filter_macros(): + macros = set() + pattern = re.compile(r'`(\w+_filter)`') + for yml_file in glob.glob(os.path.join(DETECTIONS_PATH, "*.yml")): + with open(yml_file, "r", encoding="utf-8") as f: + for match in pattern.finditer(f.read()): + macros.add(match.group(1)) + return sorted(macros) + +def read_existing_macros(): + existing = set() + if os.path.exists(SPLUNK_MACROS_CONF): + with open(SPLUNK_MACROS_CONF, "r", encoding="utf-8") as f: + for line in f: + m = re.match(r'\[(\w+)\]', line.strip()) + if m: + existing.add(m.group(1)) + return existing + +def main(): + macros = find_filter_macros() + existing = read_existing_macros() + new_macros = [m for m in macros if m not in existing] + print(f"Found {len(macros)} filter macros in ESCU rules") + print(f"Already defined: {len(existing)}") + print(f"New to add: {len(new_macros)}") + if not new_macros: + print("Nothing to add!") + return + with open(SPLUNK_MACROS_CONF, "a", encoding="utf-8") as f: + for macro_name in new_macros: + f.write(f"\n[{macro_name}]\ndefinition = search *\n") + print(f"Added {len(new_macros)} macros to {SPLUNK_MACROS_CONF}") + print("Restart Splunk for changes to take effect.") + +if __name__ == "__main__": + main() diff --git a/automation/config.py b/automation/config.py index 0ec484d..6e374f8 100644 --- a/automation/config.py +++ b/automation/config.py @@ -11,6 +11,10 @@ dotenv_path = os.path.join(PROJECT_ROOT, '.env') load_dotenv(dotenv_path=dotenv_path) +PLATFORM = os.getenv("PLATFORM", "windows").lower() + + + # Repo base: default data/repos (managed by RepoManager); override via REPOS_BASE_PATH in .env DEPENDENCIES_PATH = os.path.join(PROJECT_ROOT, 'dependencies') REPOS_BASE_PATH = os.getenv("REPOS_BASE_PATH", os.path.join(PROJECT_ROOT, "data", "repos")) @@ -50,11 +54,22 @@ def _as_bool(val: str | None, default: bool = False) -> bool: VM_USERNAME = os.getenv("VM_USERNAME") VM_PASSWORD = os.getenv("VM_PASSWORD") VM_SAFE_DIR = os.getenv("VM_SAFE_DIR") - -ATOMIC_MODULE_PATH = os.getenv("ATOMIC_MODULE_PATH", r"C:\AtomicRedTeam\invoke-atomicredteam\Invoke-AtomicRedTeam.psd1") -ATOMIC_ATOMICS_PATH = os.getenv("ATOMIC_ATOMICS_PATH", r"C:\AtomicRedTeam\atomics") +VM_SSH_PORT = int(os.getenv("VM_SSH_PORT", "22")) +VM_SSH_KEY_PATH = os.getenv("VM_SSH_KEY_PATH") +# Linux only: real network interface name of the VM (e.g. enp0s3). When set, it +# overrides an Atomic test's default "interface" argument (often eth0/ens33), +# which otherwise fails with "No such device" on a differently-named NIC. +VM_INTERFACE = os.getenv("VM_INTERFACE") + +if PLATFORM =="windows": + ATOMIC_MODULE_PATH = os.getenv("ATOMIC_MODULE_PATH", r"C:\AtomicRedTeam\invoke-atomicredteam\Invoke-AtomicRedTeam.psd1") + ATOMIC_ATOMICS_PATH = os.getenv("ATOMIC_ATOMICS_PATH", r"C:\AtomicRedTeam\atomics") +else: + ATOMIC_MODULE_PATH = None + ATOMIC_ATOMICS_PATH = os.getenv("ATOMIC_ATOMICS_PATH", os.path.join(PROJECT_ROOT, "data", "repos", "atomic-red-team", "atomics")) # --- Proxmox settings (from .env) --- +USE_PROXMOX = _as_bool(os.getenv("USE_PROXMOX"), True) PROXMOX_HOST = os.getenv("PROXMOX_HOST") PROXMOX_USER = os.getenv("PROXMOX_USER", "root") PROXMOX_PASSWORD = os.getenv("PROXMOX_PASSWORD") @@ -68,8 +83,15 @@ def _as_bool(val: str | None, default: bool = False) -> bool: SPLUNK_INDEX_WAIT_SECONDS = int(os.getenv("SPLUNK_INDEX_WAIT_SECONDS", "900")) # Time padding around execution window when querying Splunk (seconds) SPLUNK_TIME_PAD_SECONDS = int(os.getenv("SPLUNK_TIME_PAD_SECONDS", "300")) -# Post-test wait (seconds) before powering off VM to allow UF to forward events -POST_EXEC_FORWARD_WAIT_SECONDS = int(os.getenv("POST_EXEC_FORWARD_WAIT_SECONDS", "30")) +# Post-test wait (seconds) before powering off VM to allow UF to forward events. +# Linux (auditd -> UF) needs longer; Windows keeps the original 30s default so its +# run duration and search window are unchanged. +_default_forward_wait = "100" if PLATFORM == "linux" else "30" +POST_EXEC_FORWARD_WAIT_SECONDS = int(os.getenv("POST_EXEC_FORWARD_WAIT_SECONDS", _default_forward_wait)) +# Linux only: extra seconds added to the ESCU search window. Many ESCU rules +# aggregate over time (e.g. `bucket _time span=15m`) and need a window wider than +# a single test's ~100s. Default 900s (15m) to cover the common bucket span. +ESCU_AGG_WINDOW_SECONDS = int(os.getenv("ESCU_AGG_WINDOW_SECONDS", "900")) # --- Per-test verification settings --- PER_TEST_VERIFICATION = _as_bool(os.getenv("PER_TEST_VERIFICATION"), False) @@ -86,11 +108,20 @@ def _as_bool(val: str | None, default: bool = False) -> bool: # --- VM command execution timeout (seconds) --- VM_COMMAND_TIMEOUT_SECONDS = int(os.getenv("VM_COMMAND_TIMEOUT_SECONDS", "600")) -ATTACK_TIDS_DEFAULT = "T1059.001,T1087.001,T1003.001" +if PLATFORM =="windows": + ATTACK_TIDS_DEFAULT = "T1059.001,T1087.001,T1003.001" +else: + ATTACK_TIDS_DEFAULT = "T1059.004,T1087.001,T1222.002" + ATTACK_LIST = [t.strip().upper() for t in os.getenv("ATTACK_TIDS", ATTACK_TIDS_DEFAULT).split(",") if t.strip()] # --- Output paths --- -# Main report: dist/ for AJAX loading by index.html -REPORT_JSON_PATH = os.path.join(PROJECT_ROOT, "dist", "attack_rule_map.json") +# Main report: dist/ for AJAX loading by index.html. +# Only Linux gets a platform-suffixed file (separate artifact); Windows keeps the +# original un-suffixed name that index.html loads, so its behaviour is unchanged. +if PLATFORM == "linux": + REPORT_JSON_PATH = os.path.join(PROJECT_ROOT, "dist", "attack_rule_map_linux.json") +else: + REPORT_JSON_PATH = os.path.join(PROJECT_ROOT, "dist", "attack_rule_map.json") # dist/ for MITRE layer and HTML (keeps root clean) DIST_PATH = os.path.join(PROJECT_ROOT, "dist") \ No newline at end of file diff --git a/automation/dependency_handler.py b/automation/dependency_handler.py index f044ccd..692a675 100644 --- a/automation/dependency_handler.py +++ b/automation/dependency_handler.py @@ -6,6 +6,8 @@ from urllib.parse import urlparse import urllib.request +from automation import config + # Define the repositories we depend on REPOSITORIES = { "sigma": "https://github.com/SigmaHQ/sigma.git", @@ -108,13 +110,18 @@ def stage_atomic_dependencies_locally(atomic_test: dict, technique_dir: str, cac # If PathToAtomicsFolder reference, try to resolve file under repo and stage if 'PathToAtomicsFolder' in val: rel = val.split('PathToAtomicsFolder', 1)[1].lstrip('/\\') - repo_root = Path(technique_dir).parents[2] # .../dependencies/atomic-red-team/atomics/ - full_local = repo_root / rel + if config.PLATFORM == "linux": + # On Linux the atomics live at config.ATOMIC_TESTS_PATH (.../atomics), + # and rel is already "/src/...". parents[2] would overshoot to + # the repos root, so resolve directly under ATOMIC_TESTS_PATH instead. + full_local = Path(config.ATOMIC_TESTS_PATH) / rel + else: + full_local = Path(technique_dir).parents[2] / rel # .../dependencies/atomic-red-team/atomics/ if full_local.exists(): staged.append(str(full_local)) else: # Sometimes ExternalPayloads path is up one level - alt = repo_root.parent / rel + alt = Path(technique_dir).parents[2].parent / rel if alt.exists(): staged.append(str(alt)) diff --git a/automation/dynamic_generator.py b/automation/dynamic_generator.py index 01cd02b..72fa90a 100644 --- a/automation/dynamic_generator.py +++ b/automation/dynamic_generator.py @@ -47,21 +47,33 @@ "field=ScriptBlockText": "field=Message", } -# GitHub raw URLs for rule links (master branch) +# GitHub raw URLs for rule links. SIGMA_RAW_BASE = "https://raw.githubusercontent.com/SigmaHQ/sigma/master" -ESCU_RAW_BASE = "https://raw.githubusercontent.com/splunk/security_content/master" +# ESCU/security_content: the detections/ tree lives on the repo's DEFAULT `develop` +# branch; `master` 404s for the whole detections/ tree (verified: develop -> HTTP 200, +# master -> 404). Switched to `develop` for Linux only, the platform this PR covers and +# can verify. The existing Windows path is left on `master` unchanged — the same fix +# almost certainly applies to Windows too, but I did not change the existing Windows +# behaviour without confirmation / a way to test it. +if config.PLATFORM == "linux": + ESCU_RAW_BASE = "https://raw.githubusercontent.com/splunk/security_content/develop" +else: + ESCU_RAW_BASE = "https://raw.githubusercontent.com/splunk/security_content/master" def _apply_cim_mapping(spl: str) -> str: """Apply CIM-compliant field name replacements for Sigma->Splunk compatibility.""" if not spl or not isinstance(spl, str): return spl + if config.PLATFORM == "linux": + return spl result = spl for old, new in sorted(CIM_MAPPING.items(), key=lambda x: -len(x[0])): result = result.replace(old, new) return result + def _normalize_sigma_spl_for_splunk(query: str) -> str: """ pySigma çıktısını Splunk için normalize eder. @@ -141,6 +153,12 @@ def collect_for_technique(self, technique_id: str) -> tuple[list, list]: doc = utils.load_yaml_file(fp) if not isinstance(doc, dict) or "detection" not in doc or "title" not in doc: continue + + if config.PLATFORM == "linux": + product = doc.get("logsource", {}).get("product", "") + if product != "linux": + continue + tags = doc.get("tags") or [] if not isinstance(tags, list): continue @@ -154,7 +172,7 @@ def collect_for_technique(self, technique_id: str) -> tuple[list, list]: spl = _apply_cim_mapping(spl) spl = _normalize_sigma_spl_for_splunk(spl) rule_id = doc.get("id") or "" - rel_path = os.path.relpath(fp, config.SIGMA_REPO_PATH) + rel_path = os.path.relpath(fp, config.SIGMA_REPO_PATH).replace("\\", "/") rule_link = f"{SIGMA_RAW_BASE}/{rel_path}" if rel_path and not rel_path.startswith("..") else "" sigma_entries.append({ "rule_name": title, @@ -174,7 +192,14 @@ def collect_for_technique(self, technique_id: str) -> tuple[list, list]: if not search or not isinstance(search, str): continue tags = doc.get("tags") or {} - attack_ids = tags.get("mitre_attack_id") or [] + # Splunk security_content moved mitre_attack_id from under `tags` + # to the top level of the YAML (commit db8c7c8, 2026-05-13), which + # silently broke ESCU technique matching against the current repo. + # The lookup reads the new top-level field first, then falls back to + # the old `tags` location, so it is correct for both schemas and every + # platform (confirmed with the maintainer). Enabled for Windows too, + # which the current schema otherwise leaves with zero ESCU matches. + attack_ids = doc.get("mitre_attack_id") or tags.get("mitre_attack_id") or [] if isinstance(attack_ids, (str, int)): attack_ids = [str(attack_ids)] if not isinstance(attack_ids, list): @@ -184,7 +209,14 @@ def collect_for_technique(self, technique_id: str) -> tuple[list, list]: continue title = doc.get("name") or doc.get("title") or os.path.basename(fp) sanitized = self._sanitize_escu_spl(search) - rel_path = os.path.relpath(fp, config.ESCU_REPO_PATH) + if config.PLATFORM == "linux": + # Pipeline runs on a Windows host, so os.path.relpath returns + # backslashes; normalize to forward slashes so the GitHub raw URL + # is valid (the Sigma side already does this at ~line 166). Linux + # only, per the same "don't touch the Windows path" rationale above. + rel_path = os.path.relpath(fp, config.ESCU_REPO_PATH).replace("\\", "/") + else: + rel_path = os.path.relpath(fp, config.ESCU_REPO_PATH) rule_link = f"{ESCU_RAW_BASE}/{rel_path}" if rel_path and not rel_path.startswith("..") else "" file_path = rel_path if rel_path and not rel_path.startswith("..") else fp escu_entries.append({ @@ -209,7 +241,10 @@ def run_attack(technique_id: str, test_number: int = 1) -> tuple[bool, float, fl logging.warning("VM not ready for %s", technique_id) return False, 0.0, 0.0 start_time = time.time() - ok = execution_handler.run_invoke_atomic_test(technique_id, test_number) + if config.PLATFORM == "windows": + ok = execution_handler.run_invoke_atomic_test(technique_id, test_number) + else: + ok = execution_handler.run_bash_atomic_test(technique_id, test_number) if not ok: vm_handler.stop_vm() return False, start_time, time.time() @@ -328,9 +363,9 @@ def run(self) -> list: for technique_id in self.technique_ids: tid = technique_id.upper() - tests = atomic_parser.get_tests_for_technique(tid, platform_filter="windows") + tests = atomic_parser.get_tests_for_technique(tid, platform_filter=config.PLATFORM) if not tests: - logging.info("========== Technique %s (no Windows tests) ==========", tid) + logging.info(f"========== Technique {tid} (no {config.PLATFORM} tests) ==========") continue sigma_spl_list, escu_spl_list = self.rule_mapper.collect_for_technique(tid) @@ -405,12 +440,22 @@ def run(self) -> list: sigma_results[i]["detected"] = detected sigma_results[i]["log_count"] = count + # Many ESCU rules aggregate over time (e.g. `bucket _time span=15m` + # | stats ...), so they need a window wider than a single test's + # ~100s. On Linux we widen only latest_time (push forward, never into + # the past), so the test's own events still fall inside a full bucket + # without pulling in earlier tests. Each test runs on a freshly + # reverted VM, so this does not cross-contaminate. + if config.PLATFORM == "linux": + escu_latest = end_time + config.ESCU_AGG_WINDOW_SECONDS + else: + escu_latest = end_time for i, r in enumerate(escu_spl_list): detected, count = verification.run( r["sanitized_spl"], rule_name=r["rule_name"], earliest_time=start_time, - latest_time=end_time, + latest_time=escu_latest, ) escu_results[i]["detected"] = detected escu_results[i]["log_count"] = count diff --git a/automation/execution_handler.py b/automation/execution_handler.py index 750b2fb..3d6e601 100644 --- a/automation/execution_handler.py +++ b/automation/execution_handler.py @@ -5,6 +5,7 @@ import paramiko from automation import config from automation import dependency_handler +from automation import atomic_handler from automation import vm_handler @@ -13,24 +14,28 @@ def __init__(self): self.host = config.VM_HOST self.username = config.VM_USERNAME self.password = config.VM_PASSWORD - self.port = 22 + self.port = config.VM_SSH_PORT + self.key_path = config.VM_SSH_KEY_PATH self.timeout = max(30, int(config.VM_COMMAND_TIMEOUT_SECONDS)) self._client = None def connect(self): - if not self.host or not self.username or not self.password: - logging.error("VM_HOST, VM_USERNAME, VM_PASSWORD must be set in .env") + if not self.host or not self.username: + logging.error("VM_HOST and VM_USERNAME must be set in .env") return False try: self._client = paramiko.SSHClient() self._client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - self._client.connect( - hostname=self.host, - port=self.port, - username=self.username, - password=self.password, - timeout=30, - ) + if self.key_path: + self._client.connect( + hostname=self.host, port=self.port, + username=self.username, key_filename=self.key_path, + timeout=30, banner_timeout=30,) + else: + self._client.connect( + hostname=self.host, port=self.port, + username=self.username, password=self.password, + timeout=30, banner_timeout=30,) return True except Exception as e: logging.error("PowerShellExecutor connect failed: %s", e) @@ -109,6 +114,49 @@ def run_invoke_atomic_test(technique_id="T1059.001", test_number=1): executor.disconnect() return status == 0 +def run_bash_atomic_test(technique_id="T1059.004", test_number=1): + client = _create_ssh_client() + if not client: + return False + prep_commands = ( + "sudo ntpdate -u pool.ntp.org 2>/dev/null || sudo timedatectl set-ntp true; " + "sudo systemctl restart SplunkForwarder 2>/dev/null; " + "echo \"VM Current Time: $(date)\"") + status, stdout, stderr = _exec_on_vm(client, prep_commands, "bash") + client.close() + + + test_data, technique_path = atomic_handler.find_atomic_for_technique( + technique_id, config.ATOMIC_ATOMICS_PATH + ) + if not test_data or "atomic_tests" not in test_data: + logging.debug("[FAIL] No atomic test data found for %s", technique_id) + return False + + atomic_tests = test_data["atomic_tests"] + if test_number < 1 or test_number > len(atomic_tests): + logging.debug("[FAIL] Test number %s out of range", test_number) + return False + atomic_test = atomic_tests[test_number - 1] + + status, stdout, stderr = run_test_on_vm(atomic_test, technique_path) + + logging.debug("[CMD] bash atomic test %s #%s", technique_id, test_number) + stdout = re.sub(r'\x1b\[[0-9;]*m', '', stdout) if stdout else stdout + logging.debug("[STDOUT] %s", stdout) + if stderr: + logging.debug("[STDERR] %s", stderr) + if status == 0: + logging.debug("[SUCCESS] Bash atomic test %s completed", technique_id) + else: + logging.debug("[FAIL] Bash atomic test exit code %s", status) + # Return the real exit status (like the Windows path) so the caller skips + # detection for a test that did not run cleanly. Note: a few tests exit + # non-zero yet still emit the execve telemetry a command-based rule matches + # (e.g. xclip/xwd erroring on a headless VM with no X11 display); those + # detections will no longer be counted, which is the correct, stricter behaviour. + return status == 0 + def run_simple_encoded_command(): script = "Write-Host 'AttackRuleMap-Simulation'; Get-Date -Format 'yyyy-MM-dd HH:mm:ss'; whoami" @@ -129,11 +177,15 @@ def run_simple_encoded_command(): def run_first_attack_simulation(): - if run_invoke_atomic_test("T1059.001", 1): - return True - logging.debug("[FALLBACK] Invoke-AtomicTest failed, running simple EncodedCommand...") - return run_simple_encoded_command() - + if config.PLATFORM == "windows": + if run_invoke_atomic_test("T1059.001", 1): + return True + logging.debug("[FALLBACK] Invoke-AtomicTest failed, running simple EncodedCommand...") + return run_simple_encoded_command() + else: + if run_bash_atomic_test("T1059.004", 1): + return True + return False def run_first_attack_workflow(): logging.debug("Starting first attack simulation workflow...") @@ -166,11 +218,19 @@ def _create_ssh_client(): try: client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - client.connect( - hostname=config.VM_HOST, port=22, - username=config.VM_USERNAME, password=config.VM_PASSWORD, - timeout=30 - ) + + + if config.VM_SSH_KEY_PATH: + client.connect( + hostname=config.VM_HOST, port=config.VM_SSH_PORT, + username=config.VM_USERNAME, key_filename=config.VM_SSH_KEY_PATH, + timeout=30, banner_timeout=30,) + else: + client.connect( + hostname=config.VM_HOST, port=config.VM_SSH_PORT, + username=config.VM_USERNAME, password=config.VM_PASSWORD, + timeout=30, banner_timeout=30,) + return client except Exception as e: logging.error(f"Failed to create SSH client. Exception: {e}") @@ -194,6 +254,7 @@ def _build_arg_value_map(atomic_test: dict, safe_dir: str) -> dict: - Replaces PathToAtomicsFolder references with files uploaded into safe_dir. - If value looks like a file path (.exe, .dll, .ps1, etc.), point it to safe_dir\filename """ + sep = "\\" if config.PLATFORM == "windows" else "/" mapping = {} for arg_name, arg_details in (atomic_test.get('input_arguments') or {}).items(): default_value = str(arg_details.get('default', '')) @@ -202,10 +263,14 @@ def _build_arg_value_map(atomic_test: dict, safe_dir: str) -> dict: relative_path = default_value.split("PathToAtomicsFolder", 1)[1].strip('\\/') # This assumes the relative path is from the root of the atomic-red-team repo file_name = os.path.basename(relative_path.replace('\\', '/')) - rewritten_path = f"{safe_dir}\\{file_name}" - elif any(ext in default_value.lower() for ext in ['.exe', '.dll', '.dmp', '.ps1', '.bat', '.txt', '.csv', '.zip']): + rewritten_path = f"{safe_dir}{sep}{file_name}" + elif any(ext in default_value.lower() for ext in ['.exe', '.dll', '.dmp', '.ps1', '.bat', '.txt', '.csv', '.zip', '.sh', '.py', '.so']): file_name = os.path.basename(default_value.replace('\\', '/')) - rewritten_path = f"{safe_dir}\\{file_name}" + rewritten_path = f"{safe_dir}{sep}{file_name}" + elif config.PLATFORM == "linux" and arg_name == "interface" and config.VM_INTERFACE: + # Atomic's default interface (eth0/ens33) often doesn't exist on the VM. + # If VM_INTERFACE is configured, use the real NIC name instead. + rewritten_path = config.VM_INTERFACE else: rewritten_path = default_value @@ -223,7 +288,11 @@ def _apply_rewrites_to_command(cmd_text: str, arg_map: dict, safe_dir: str) -> s out = out.replace(ph, val) # Replace PathToAtomicsFolder tokens with C:\\Atomic-Tests first (canonical), then ensure any path-like # values referring to ExternalPayloads map to our safe_dir uploads as a fallback. - out = out.replace('PathToAtomicsFolder', 'C:\\Atomic-Tests') + if config.PLATFORM == "windows": + out = out.replace('PathToAtomicsFolder', 'C:\\Atomic-Tests') + else: + out = out.replace('PathToAtomicsFolder', '/tmp/atomic-tests') + return out @@ -237,15 +306,39 @@ def _normalize_command_for_executor(command_text: str, executor_name: str) -> st return '; '.join(lines) elif executor_name == 'cmd': return ' & '.join(lines) + elif executor_name in ('bash', 'sh'): + return '\n'.join(lines) return command_text def _exec_on_vm(client, command_text: str, executor_name: str): """Execute the given command on VM using specified executor (powershell/cmd) with timeout.""" + # Linux only: when True, the script is sent over the channel's stdin instead + # of being embedded in `-c "..."` (avoids inner-quote collision). See below. + feed_via_stdin = False if executor_name == 'powershell': full_command_to_run = f"powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command \"{command_text}\"" elif executor_name == 'cmd': full_command_to_run = f"cmd /c \"{command_text}\"" + elif executor_name in ('bash', 'sh'): + if config.PLATFORM == "windows": + full_command_to_run = f"{executor_name} -c \"{command_text}\"" + else: + # Linux: wrapping the script in -c "..." breaks whenever the script + # itself contains double quotes (e.g. `if [ "$(uname)" = 'FreeBSD' ]`): + # the inner " closes the outer " early and the shell mis-parses the + # script (Syntax error: unexpected end of file). To stay safe we only + # change behaviour for scripts that contain a double quote: we run + # `sh -s` and feed the script via stdin, so there is no outer quoting + # to collide with. Quote-free scripts keep the original -c path, so + # the common case is byte-for-byte unchanged. Windows path untouched + # (I cannot test it, so I did not change it without confirmation). + if '"' in command_text: + full_command_to_run = f"sudo {executor_name} -s" + feed_via_stdin = True + else: + full_command_to_run = f"sudo {executor_name} -c \"{command_text}\"" + else: return (1, "", f"Unsupported executor: {executor_name}") @@ -260,6 +353,13 @@ def _exec_on_vm(client, command_text: str, executor_name: str): chan.settimeout(timeout) chan.exec_command(full_command_to_run) + if feed_via_stdin: + # Push the script into `sh -s` over stdin, then close the write side + # so the shell sees EOF and starts executing. No outer quoting, so + # the script's own quotes can never collide. + chan.sendall(command_text + "\n") + chan.shutdown_write() + stdout_chunks = [] stderr_chunks = [] start_time = time.time() @@ -297,21 +397,30 @@ def run_test_on_vm(atomic_test, test_technique_path): if not client: return (1, "", "Could not establish SSH connection.") - safe_dir = config.VM_SAFE_DIR or "C:\\Atomic-Tests" + if config.PLATFORM == "windows": + safe_dir = config.VM_SAFE_DIR or "C:\\Atomic-Tests" + else: + safe_dir = config.VM_SAFE_DIR or "/tmp/atomic-tests" + # Ensure safe dir exists on remote try: - stdin, stdout, stderr = client.exec_command(f'powershell -Command "New-Item -Path \"{safe_dir}\" -ItemType Directory -Force | Out-Null"') + if config.PLATFORM == "windows": + stdin, stdout, stderr = client.exec_command(f'powershell -Command "New-Item -Path \"{safe_dir}\" -ItemType Directory -Force | Out-Null"') + else: + stdin, stdout, stderr = client.exec_command(f'mkdir -p {safe_dir}') + stdout.channel.recv_exit_status() except Exception as e: logging.warning(f" -> Could not ensure remote safe dir exists: {e}") # Seed C:\Atomic-Tests path as well (many atomics assume it) - try: - stdin, stdout, stderr = client.exec_command('powershell -Command "New-Item -Path \"C:\\Atomic-Tests\" -ItemType Directory -Force | Out-Null"') - stdout.channel.recv_exit_status() - except Exception as e: - logging.debug(f" -> Could not create C:\\Atomic-Tests: {e}") + if config.PLATFORM == "windows": + try: + stdin, stdout, stderr = client.exec_command('powershell -Command "New-Item -Path \"C:\\Atomic-Tests\" -ItemType Directory -Force | Out-Null"') + stdout.channel.recv_exit_status() + except Exception as e: + logging.debug(f" -> Could not create C:\\Atomic-Tests: {e}") # --- 1. Handle Dependencies --- # 1a) Resolve and stage locally (download URLs / ExternalPayloads) @@ -321,10 +430,11 @@ def run_test_on_vm(atomic_test, test_technique_path): try: remote_filename = os.path.basename(lf.replace('\\', '/')) remote_path_safe = f"{safe_dir.replace('\\','/')}/{remote_filename}" - remote_path_atomic = f"C:/Atomic-Tests/{remote_filename}" _upload_file_sftp(client, lf, remote_path_safe) - # also copy into C:\Atomic-Tests for tests that reference that path - client.exec_command(f'powershell -Command "Copy-Item -Force \"{remote_path_safe}\" -Destination \"{remote_path_atomic}\""') + if config.PLATFORM == "windows": + remote_path_atomic = f"C:/Atomic-Tests/{remote_filename}" + # also copy into C:\Atomic-Tests for tests that reference that path + client.exec_command(f'powershell -Command "Copy-Item -Force \"{remote_path_safe}\" -Destination \"{remote_path_atomic}\""') except Exception as e: logging.warning(f" -> Failed to upload staged file '{lf}': {e}") if 'dependencies' in atomic_test and atomic_test['dependencies']: @@ -350,6 +460,8 @@ def run_test_on_vm(atomic_test, test_technique_path): # --- 2. Run dependency prereq commands if defined --- dep_executor = atomic_test.get('dependency_executor_name', 'powershell') + if config.PLATFORM != "windows" and dep_executor in ('powershell', 'command_prompt'): + dep_executor = 'bash' arg_map = _build_arg_value_map(atomic_test, safe_dir) for dep in (atomic_test.get('dependencies') or []): prereq_cmd = dep.get('prereq_command') diff --git a/automation/report_handler.py b/automation/report_handler.py index e437cf0..92008b9 100644 --- a/automation/report_handler.py +++ b/automation/report_handler.py @@ -89,12 +89,15 @@ def build_layer(name: str, description: str, detected_key: str) -> dict: "enabled": True, "metadata": [] }) + # Linux is aligned to ATT&CK v19 (includes T1685); Navigator won't draw a + # technique newer than the layer's attack version. Windows stays on "18". + attack_version = "19" if config.PLATFORM == "linux" else "18" return { "name": name, - "versions": {"attack": "18", "navigator": "5.3.0", "layer": "4.5"}, + "versions": {"attack": attack_version, "navigator": "5.3.0", "layer": "4.5"}, "domain": "enterprise-attack", "description": description, - "filters": {"platforms": ["Windows"]}, + "filters": {"platforms": [config.PLATFORM.capitalize()]}, "sorting": 3, "layout": { "layout": "side", @@ -130,10 +133,13 @@ def build_layer(name: str, description: str, detected_key: str) -> dict: except OSError as e: logging.warning("Could not remove legacy mitre_layer.json: %s", e) + # Only Linux gets a platform-suffixed file (separate artifact); Windows keeps the + # original un-suffixed names that index.html loads, so its behaviour is unchanged. + suffix = "_linux" if config.PLATFORM == "linux" else "" layers_config = [ - ("mitre_layer_sigma.json", "ARM - Sigma Detection Coverage", "Sigma rule coverage", "sigma"), - ("mitre_layer_splunk.json", "ARM - Splunk Detection Coverage", "Splunk/ESCU rule coverage", "splunk"), - ("mitre_layer_combined.json", "ARM - Sigma + Splunk Detection Coverage", "Sigma OR Splunk coverage", "combined"), + (f"mitre_layer_sigma{suffix}.json", "ARM - Sigma Detection Coverage", "Sigma rule coverage", "sigma"), + (f"mitre_layer_splunk{suffix}.json", "ARM - Splunk Detection Coverage", "Splunk/ESCU rule coverage", "splunk"), + (f"mitre_layer_combined{suffix}.json", "ARM - Sigma + Splunk Detection Coverage", "Sigma OR Splunk coverage", "combined"), ] output_paths = [] @@ -303,10 +309,13 @@ def _normalize_entry(e: dict) -> dict: json.dump(lite, f, separators=(",", ":"), ensure_ascii=False) logging.info("Report saved (smart merge, ultra-lite): %s", path) - # Write metadata.json in same directory (dist/) for dashboard "Last Updated" + # Write metadata for the dashboard "Last Updated". Only Linux gets a + # platform-suffixed file so it doesn't overwrite the Windows metadata.json + # the site reads (Windows behaviour stays unchanged). timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") metadata = {"last_updated": timestamp} - metadata_path = os.path.join(os.path.dirname(path), "metadata.json") + metadata_name = "metadata_linux.json" if config.PLATFORM == "linux" else "metadata.json" + metadata_path = os.path.join(os.path.dirname(path), metadata_name) with open(metadata_path, "w", encoding="utf-8") as f: json.dump(metadata, f, indent=2) logging.info("Metadata saved: %s", metadata_path) @@ -324,10 +333,27 @@ def print_coverage_stats(self, data: list) -> None: return total_tests = len(data) - detected_tests = sum( - 1 for t in data - if any(r.get("detected") for r in t.get("sigma_rules", [])) - ) + # The original counter marks a test as detected only when a Sigma rule + # fired, ignoring escu_rules/splunk_rules entirely. ESCU is a rule type + # this project also collects and verifies, so a test detected only by an + # ESCU rule (e.g. T1016, whose Sigma rule does not fire but whose ESCU + # rule does) was wrongly reported as "0 detected" on the console even + # though it is recorded in the JSON and the MITRE layers. On Linux we + # count a test as detected if a Sigma OR an ESCU rule fired, matching how + # generate_mitre_layers already scores coverage. I left the Windows path + # exactly as it was, since I cannot run/verify Windows and did not want + # to change its console numbers without confirmation. + if config.PLATFORM == "linux": + detected_tests = sum( + 1 for t in data + if any(r.get("detected") for r in t.get("sigma_rules", [])) + or any(r.get("detected") for r in (t.get("escu_rules") or t.get("splunk_rules") or [])) + ) + else: + detected_tests = sum( + 1 for t in data + if any(r.get("detected") for r in t.get("sigma_rules", [])) + ) coverage_pct = int(round((detected_tests / total_tests * 100))) if total_tests > 0 else 0 logging.info("Total Atomic Tests: %s", total_tests) diff --git a/automation/sigma_handler.py b/automation/sigma_handler.py index 6e2d565..ec54c60 100644 --- a/automation/sigma_handler.py +++ b/automation/sigma_handler.py @@ -3,6 +3,7 @@ import re import logging from automation import utils +from automation import config SIGMA_BASE_URL = "https://github.com/SigmaHQ/sigma/blob/main/rules/" @@ -16,7 +17,12 @@ def parse_sigma_rule(file_path, rules_base_path): if not isinstance(rule_content, dict) or not all(k in rule_content for k in ['title', 'detection', 'logsource']): return None + + platform = rule_content.get('logsource', {}).get('product', 'N/A') + if config.PLATFORM == "linux" and platform != "linux": + return None + attack_tags = [] if 'tags' in rule_content: for tag in rule_content['tags']: @@ -30,8 +36,6 @@ def parse_sigma_rule(file_path, rules_base_path): relative_path = os.path.relpath(file_path, start=rules_base_path).replace('\\', '/') rule_link = SIGMA_BASE_URL + relative_path - - platform = rule_content.get('logsource', {}).get('product', 'N/A') return { 'filepath': file_path, diff --git a/automation/vm_handler.py b/automation/vm_handler.py index c14bbaf..ad792f5 100644 --- a/automation/vm_handler.py +++ b/automation/vm_handler.py @@ -7,7 +7,7 @@ import re import paramiko from automation import config - +import subprocess def _get_proxmox_ssh_client(): """ @@ -64,6 +64,8 @@ def _run_proxmox_command(args, check=True): args: list of command parts, e.g. ["rollback", "100", "Lab-Ready-v1"] Returns (success: bool, stdout: str, stderr: str) """ + + vm_id = config.TARGET_VM_ID if not vm_id: logging.error("TARGET_VM_ID must be set in .env") @@ -99,16 +101,25 @@ def _run_proxmox_command(args, check=True): def get_vm_state(): """Gets the current state of the VM on Proxmox (running/stopped).""" - success, stdout, _ = _run_proxmox_command(["status", config.TARGET_VM_ID], check=False) - if not success or not stdout: + if config.USE_PROXMOX: + success, stdout, _ = _run_proxmox_command(["status", config.TARGET_VM_ID], check=False) + if not success or not stdout: + return "unknown" + # qm status returns e.g. "status: running" or "status: stopped" + match = re.search(r"status:\s*(\w+)", stdout, re.IGNORECASE) + if match: + return match.group(1).lower() return "unknown" - # qm status returns e.g. "status: running" or "status: stopped" - match = re.search(r"status:\s*(\w+)", stdout, re.IGNORECASE) - if match: - return match.group(1).lower() - return "unknown" - - + else: + result = subprocess.run(["VBoxManage", "showvminfo", config.TARGET_VM_ID, "--machinereadable"], + capture_output=True, text=True) + if result.returncode != 0: + return "unknown" + match = re.search(r'VMState="(\w+)"', result.stdout) + if match: + return match.group(1).lower() + return "unknown" + def ensure_vm_is_off(timeout_seconds=60): """ Ensures the VM is in a stopped state. @@ -121,17 +132,23 @@ def ensure_vm_is_off(timeout_seconds=60): state = get_vm_state() logging.debug("Current VM state: %s", state) - if state == "stopped": + if state == "stopped" or state == "poweroff" or state == "aborted" or state == "saved": logging.debug("VM is already stopped.") return True elif state == "running": logging.debug("VM is running. Sending stop command...") - success, _, err = _run_proxmox_command(["stop", config.TARGET_VM_ID]) - if not success: - logging.warning(f"Stop command may have failed: {err}") + if config.USE_PROXMOX: + success, _, err = _run_proxmox_command(["stop", config.TARGET_VM_ID]) + if not success: + logging.warning(f"Stop command may have failed: {err}") + else: + result = subprocess.run( + ["VBoxManage", "controlvm", config.TARGET_VM_ID, "poweroff"], + capture_output=True, text=True) + if result.returncode != 0: + logging.warning(f"Stop command may have failed: {result.stderr}") else: logging.warning(f"VM is in state '{state}'. Waiting...") - time.sleep(5) logging.error("Failed to get VM into stopped state within the timeout.") @@ -145,21 +162,30 @@ def revert_to_snapshot(): if not snapshot or not vm_id: logging.error("TARGET_SNAPSHOT and TARGET_VM_ID must be set in .env") return False - - logging.debug("Reverting VM %s to snapshot '%s'...", vm_id, snapshot) - - if not ensure_vm_is_off(): - logging.error("Cannot restore snapshot because VM could not be stopped.") - return False - - success, _, err = _run_proxmox_command(["rollback", vm_id, snapshot]) - if not success: - logging.error(f"Snapshot rollback failed: {err}") - return False - - logging.debug("Verifying state after snapshot rollback...") - return ensure_vm_is_off() - + if config.USE_PROXMOX: + logging.debug("Reverting VM %s to snapshot '%s'...", vm_id, snapshot) + if not ensure_vm_is_off(): + logging.error("Cannot restore snapshot because VM could not be stopped.") + return False + success, _, err = _run_proxmox_command(["rollback", vm_id, snapshot]) + if not success: + logging.error(f"Snapshot rollback failed: {err}") + return False + logging.debug("Verifying state after snapshot rollback...") + return ensure_vm_is_off() + else: + logging.debug("Reverting VM %s to snapshot '%s' via VBoxManage...", vm_id, snapshot) + if not ensure_vm_is_off(): + logging.error("Cannot restore snapshot because VM could not be stopped.") + return False + result = subprocess.run( + ["VBoxManage", "snapshot", vm_id, "restore", snapshot], + capture_output=True, text=True + ) + if result.returncode != 0: + logging.error(f"Snapshot restore failed: {result.stderr}") + return False + return True def start_vm(): """Starts the VM on Proxmox.""" @@ -167,20 +193,28 @@ def start_vm(): if not vm_id: logging.error("TARGET_VM_ID must be set in .env") return False - - logging.debug("Starting VM %s...", vm_id) - success, _, err = _run_proxmox_command(["start", vm_id]) - if not success: - logging.error(f"Failed to start VM: {err}") - return success - - + if config.USE_PROXMOX: + logging.debug("Starting VM %s...", vm_id) + success, _, err = _run_proxmox_command(["start", vm_id]) + if not success: + logging.error(f"Failed to start VM: {err}") + return success + else: + logging.debug("Starting VM %s via VBoxManage...", vm_id) + result = subprocess.run( + ["VBoxManage", "startvm", vm_id, "--type", "headless"], + capture_output=True, text=True + ) + if result.returncode != 0: + logging.error(f"Failed to start VM: {result.stderr}") + return False + return True + def stop_vm(): """Stops the VM after test case.""" logging.debug("Stopping VM after test case...") return ensure_vm_is_off() - def is_vm_ready(timeout_seconds=300): """ Checks if the VM (Windows guest) is booted and SSH port is responding. @@ -193,13 +227,24 @@ def is_vm_ready(timeout_seconds=300): try: client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - client.connect( - hostname=config.VM_HOST, - port=22, - username=config.VM_USERNAME, - password=config.VM_PASSWORD, - timeout=10, - ) + if config.VM_SSH_KEY_PATH: + client.connect( + hostname=config.VM_HOST, + port=config.VM_SSH_PORT, + username=config.VM_USERNAME, + key_filename=config.VM_SSH_KEY_PATH, + timeout=10, + banner_timeout=30, + ) + else: + client.connect( + hostname=config.VM_HOST, + port=config.VM_SSH_PORT, + username=config.VM_USERNAME, + password=config.VM_PASSWORD, + timeout=10, + banner_timeout=30, + ) client.close() logging.debug("VM is ready and responding to SSH.") return True diff --git a/dist/attack_rule_map_linux.json b/dist/attack_rule_map_linux.json new file mode 100644 index 0000000..5abb226 --- /dev/null +++ b/dist/attack_rule_map_linux.json @@ -0,0 +1 @@ +[{"tech_id":"T1222.002","test_number":1,"atomic_attack_guid":"34ca1464-de9d-40c6-8c77-690adf36a135","atomic_attack_name":"chmod - Change file or folder mode (numeric mode)","platform":"linux,macos","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":2,"atomic_attack_guid":"fc9d6695-d022-4a80-91b1-381f5c35aff3","atomic_attack_name":"chmod - Change file or folder mode (symbolic mode)","platform":"linux,macos","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":3,"atomic_attack_guid":"ea79f937-4a4d-4348-ace6-9916aec453a4","atomic_attack_name":"chmod - Change file or folder mode (numeric mode) recursively","platform":"linux,macos","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":4,"atomic_attack_guid":"0451125c-b5f6-488f-993b-5a32b09f7d8f","atomic_attack_name":"chmod - Change file or folder mode (symbolic mode) recursively","platform":"linux,macos","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":5,"atomic_attack_guid":"d169e71b-85f9-44ec-8343-27093ff3dfc0","atomic_attack_name":"chown - Change file or folder ownership and group","platform":"macos,linux","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":6,"atomic_attack_guid":"b78598be-ff39-448f-a463-adbf2a5b7848","atomic_attack_name":"chown - Change file or folder ownership and group recursively","platform":"macos,linux","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":7,"atomic_attack_guid":"967ba79d-f184-4e0e-8d09-6362b3162e99","atomic_attack_name":"chown - Change file or folder mode ownership only","platform":"linux,macos","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":8,"atomic_attack_guid":"3b015515-b3d8-44e9-b8cd-6fa84faf30b2","atomic_attack_name":"chown - Change file or folder ownership recursively","platform":"macos,linux","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":9,"atomic_attack_guid":"e7469fe2-ad41-4382-8965-99b94dd3c13f","atomic_attack_name":"chattr - Remove immutable file attribute","platform":"macos,linux","sigma_rules":[{"rule_name":"Remove Immutable File Attribute - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_chattr_immutable_removal.yml"},{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":10,"atomic_attack_guid":"60eee3ea-2ebd-453b-a666-c52ce08d2709","atomic_attack_name":"chflags - Remove immutable file attribute","platform":"linux","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":11,"atomic_attack_guid":"973631cf-6680-4ffa-a053-045e1b6b67ab","atomic_attack_name":"Chmod through c script","platform":"macos,linux","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":12,"atomic_attack_guid":"da40b5fe-3098-4b3b-a410-ff177e49ee2e","atomic_attack_name":"Chmod through c script (freebsd)","platform":"linux","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":13,"atomic_attack_guid":"18592ba1-5f88-4e3c-abc8-ab1c6042e389","atomic_attack_name":"Chown through c script","platform":"macos,linux","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1222.002","test_number":14,"atomic_attack_guid":"eb577a19-b730-4918-9b03-c5edcf51dc4e","atomic_attack_name":"Chown through c script (freebsd)","platform":"linux","sigma_rules":[{"rule_name":"File or Folder Permissions Change","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_file_or_folder_permissions.yml"}],"splunk_rules":[]},{"tech_id":"T1027.001","test_number":1,"atomic_attack_guid":"ffe2346c-abd5-4b45-a713-bf5f1ebd573a","atomic_attack_name":"Pad Binary to Change Hash - Linux/macOS dd","platform":"linux,macos","sigma_rules":[{"rule_name":"Binary Padding - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_binary_padding.yml"}],"splunk_rules":[]},{"tech_id":"T1027.001","test_number":2,"atomic_attack_guid":"e22a9e89-69c7-410f-a473-e6c212cd2292","atomic_attack_name":"Pad Binary to Change Hash using truncate command - Linux/macOS","platform":"linux,macos","sigma_rules":[{"rule_name":"Binary Padding - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_binary_padding.yml"}],"splunk_rules":[]},{"tech_id":"T1030","test_number":1,"atomic_attack_guid":"ab936c51-10f4-46ce-9144-e02137b2016a","atomic_attack_name":"Data Transfer Size Limits","platform":"macos,linux","sigma_rules":[{"rule_name":"Split A File Into Pieces - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/syscall/lnx_auditd_split_file_into_pieces.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Data Transfer Size Limits Via Split Syscall","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_data_transfer_size_limits_via_split_syscall.yml"}]},{"tech_id":"T1033","test_number":2,"atomic_attack_guid":"2a9b677d-a230-44f4-ad86-782df1ef108c","atomic_attack_name":"System Owner/User Discovery","platform":"linux,macos","sigma_rules":[{"rule_name":"System Owner or User Discovery - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_user_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1070.006","test_number":4,"atomic_attack_guid":"631ea661-d661-44b0-abdb-7a7f3fc08e50","atomic_attack_name":"Modify file timestamps using reference file","platform":"linux,macos","sigma_rules":[{"rule_name":"File Time Attribute Change - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_change_file_time_attr.yml"}],"splunk_rules":[]},{"tech_id":"T1082","test_number":3,"atomic_attack_guid":"cccb070c-df86-4216-a5bc-9fb60c74e27c","atomic_attack_name":"List OS Information","platform":"linux,macos","sigma_rules":[{"rule_name":"System Information Discovery - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_system_info_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1082","test_number":4,"atomic_attack_guid":"31dad7ad-2286-4c02-ae92-274418c85fec","atomic_attack_name":"Linux VM Check via Hardware","platform":"linux","sigma_rules":[{"rule_name":"System Information Discovery - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_system_info_discovery.yml"},{"rule_name":"System Info Discovery via Sysinfo Syscall","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/syscall/lnx_auditd_susp_discovery_sysinfo_syscall.yml"}],"splunk_rules":[]},{"tech_id":"T1082","test_number":5,"atomic_attack_guid":"8057d484-0fae-49a4-8302-4812c4f1e64e","atomic_attack_name":"Linux VM Check via Kernel Modules","platform":"linux","sigma_rules":[{"rule_name":"System Information Discovery - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_system_info_discovery.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Kernel Module Enumeration","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_kernel_module_enumeration.yml"}]},{"tech_id":"T1082","test_number":6,"atomic_attack_guid":"eefe6a49-d88b-41d8-8fc2-b46822da90d3","atomic_attack_name":"FreeBSD VM Check via Kernel Modules","platform":"linux","sigma_rules":[{"rule_name":"System Information Discovery - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_system_info_discovery.yml"},{"rule_name":"System Info Discovery via Sysinfo Syscall","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/syscall/lnx_auditd_susp_discovery_sysinfo_syscall.yml"}],"splunk_rules":[]},{"tech_id":"T1082","test_number":8,"atomic_attack_guid":"486e88ea-4f56-470f-9b57-3f4d73f39133","atomic_attack_name":"Hostname Discovery","platform":"linux,macos","sigma_rules":[{"rule_name":"System Information Discovery - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_system_info_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1082","test_number":12,"atomic_attack_guid":"fcbdd43f-f4ad-42d5-98f3-0218097e2720","atomic_attack_name":"Environment variables discovery on freebsd, macos and linux","platform":"linux,macos","sigma_rules":[{"rule_name":"System Information Discovery - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_system_info_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1082","test_number":25,"atomic_attack_guid":"034fe21c-3186-49dd-8d5d-128b35f181c7","atomic_attack_name":"Linux List Kernel Modules","platform":"linux","sigma_rules":[{"rule_name":"System Information Discovery - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_system_info_discovery.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Kernel Module Enumeration","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_kernel_module_enumeration.yml"}]},{"tech_id":"T1082","test_number":26,"atomic_attack_guid":"4947897f-643a-4b75-b3f5-bed6885749f6","atomic_attack_name":"FreeBSD List Kernel Modules","platform":"linux","sigma_rules":[{"rule_name":"System Information Discovery - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_system_info_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1489","test_number":4,"atomic_attack_guid":"42e3a5bd-1e45-427f-aa08-2a65fa29a820","atomic_attack_name":"Linux - Stop service using systemctl","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Stop Services","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_stop_services.yml"}]},{"tech_id":"T1489","test_number":5,"atomic_attack_guid":"e5d95be6-02ee-4ff1-aebe-cf86013b6189","atomic_attack_name":"Linux - Stop service by killing process using killall","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Stop Services","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_stop_services.yml"}]},{"tech_id":"T1489","test_number":6,"atomic_attack_guid":"332f4c76-7e96-41a6-8cc2-7361c49db8be","atomic_attack_name":"Linux - Stop service by killing process using kill","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Stop Services","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_stop_services.yml"}]},{"tech_id":"T1489","test_number":7,"atomic_attack_guid":"08b4718f-a8bf-4bb5-a552-294fc5178fea","atomic_attack_name":"Linux - Stop service by killing process using pkill","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Stop Services","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_stop_services.yml"}]},{"tech_id":"T1489","test_number":8,"atomic_attack_guid":"6e76f56f-2373-4a6c-a63f-98b7b72761f1","atomic_attack_name":"Abuse of linux magic system request key for Send a SIGTERM to all processes","platform":"linux","sigma_rules":[{"rule_name":"Potential Abuse of Linux Magic System Request Key","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_magic_system_request_key.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Stop Services","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_stop_services.yml"},{"rule_name":"Linux Magic SysRq Key Abuse","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_magic_sysrq_key_abuse.yml"}]},{"tech_id":"T1529","test_number":3,"atomic_attack_guid":"6326dbc4-444b-4c04-88f4-27e94d0327cb","atomic_attack_name":"Restart System via `shutdown` - FreeBSD/macOS/Linux","platform":"linux,macos","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1529","test_number":4,"atomic_attack_guid":"4963a81e-a3ad-4f02-adda-812343b351de","atomic_attack_name":"Shutdown System via `shutdown` - FreeBSD/macOS/Linux","platform":"linux,macos","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1529","test_number":5,"atomic_attack_guid":"47d0b042-a918-40ab-8cf9-150ffe919027","atomic_attack_name":"Restart System via `reboot` - FreeBSD/macOS/Linux","platform":"linux,macos","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1529","test_number":6,"atomic_attack_guid":"918f70ab-e1ef-49ff-bc57-b27021df84dd","atomic_attack_name":"Shutdown System via `halt` - FreeBSD/Linux","platform":"linux","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1529","test_number":7,"atomic_attack_guid":"7b1cee42-320f-4890-b056-d65c8b884ba5","atomic_attack_name":"Reboot System via `halt` - FreeBSD","platform":"linux","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1529","test_number":8,"atomic_attack_guid":"78f92e14-f1e9-4446-b3e9-f1b921f2459e","atomic_attack_name":"Reboot System via `halt` - Linux","platform":"linux","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1529","test_number":9,"atomic_attack_guid":"73a90cd2-48a2-4ac5-8594-2af35fa909fa","atomic_attack_name":"Shutdown System via `poweroff` - FreeBSD/Linux","platform":"linux","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1529","test_number":10,"atomic_attack_guid":"5a282e50-86ff-438d-8cef-8ae01c9e62e1","atomic_attack_name":"Reboot System via `poweroff` - FreeBSD","platform":"linux","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1529","test_number":16,"atomic_attack_guid":"d2a1f4bc-a064-4223-8281-a086dce5423c","atomic_attack_name":"Abuse of Linux Magic System Request Key for Reboot","platform":"linux","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1543.002","test_number":1,"atomic_attack_guid":"d9e4f24f-aa67-4c6e-bcbf-85622b697a7c","atomic_attack_name":"Create Systemd Service","platform":"linux","sigma_rules":[{"rule_name":"Service Reload or Start - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_susp_service_reload_or_restart.yml"},{"rule_name":"Systemd Service Creation","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_systemd_service_creation.yml"}],"splunk_rules":[]},{"tech_id":"T1543.002","test_number":2,"atomic_attack_guid":"760fe8d2-79d9-494f-905e-a239a3df86f6","atomic_attack_name":"Create SysV Service","platform":"linux","sigma_rules":[{"rule_name":"Service Reload or Start - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_susp_service_reload_or_restart.yml"}],"splunk_rules":[]},{"tech_id":"T1543.002","test_number":3,"atomic_attack_guid":"c35ac4a8-19de-43af-b9f8-755da7e89c89","atomic_attack_name":"Create Systemd Service file, Enable the service , Modify and Reload the service.","platform":"linux","sigma_rules":[{"rule_name":"Service Reload or Start - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_susp_service_reload_or_restart.yml"}],"splunk_rules":[]},{"tech_id":"T1552.001","test_number":3,"atomic_attack_guid":"bd4cf0d1-7646-474e-8610-78ccf5a097c4","atomic_attack_name":"Extract passwords with grep","platform":"linux,macos","sigma_rules":[{"rule_name":"Credentials In Files - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_find_cred_in_files.yml"}],"splunk_rules":[]},{"tech_id":"T1552.003","test_number":1,"atomic_attack_guid":"3cfde62b-7c33-4b26-a61e-755d6131c8ce","atomic_attack_name":"Search Through Bash History","platform":"linux,macos","sigma_rules":[{"rule_name":"Suspicious History File Operations - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_susp_histfile_operations.yml"}],"splunk_rules":[]},{"tech_id":"T1552.003","test_number":2,"atomic_attack_guid":"d87d3b94-05b4-40f2-a80f-99864ffa6803","atomic_attack_name":"Search Through sh History","platform":"linux","sigma_rules":[{"rule_name":"Suspicious History File Operations - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_susp_histfile_operations.yml"}],"splunk_rules":[]},{"tech_id":"T1560.001","test_number":6,"atomic_attack_guid":"cde3c2af-3485-49eb-9c1f-0ed60e9cc0af","atomic_attack_name":"Data Compressed - nix - gzip Single File","platform":"linux,macos","sigma_rules":[{"rule_name":"Data Compressed","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_data_compressed.yml"}],"splunk_rules":[]},{"tech_id":"T1560.001","test_number":8,"atomic_attack_guid":"0286eb44-e7ce-41a0-b109-3da516e05a5f","atomic_attack_name":"Data Encrypted with zip and gpg symmetric","platform":"linux,macos","sigma_rules":[{"rule_name":"Data Compressed","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_data_compressed.yml"}],"splunk_rules":[]},{"tech_id":"T1560.001","test_number":9,"atomic_attack_guid":"a743e3a6-e8b2-4a30-abe7-ca85d201b5d3","atomic_attack_name":"Encrypts collected data with AES-256 and Base64","platform":"linux,macos","sigma_rules":[{"rule_name":"Data Compressed","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_data_compressed.yml"}],"splunk_rules":[]},{"tech_id":"T1685","test_number":59,"atomic_attack_guid":"ac333fe1-ce2b-400b-a117-538634427439","atomic_attack_name":"Disable ASLR Via sysctl parameters - Linux","platform":"linux","sigma_rules":[{"rule_name":"ASLR Disabled Via Sysctl or Direct Syscall - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_disable_aslr_protection.yml"}],"splunk_rules":[]},{"tech_id":"T1564.001","test_number":1,"atomic_attack_guid":"61a782e5-9a19-40b5-8ba4-69a4b9f3d7be","atomic_attack_name":"Create a hidden file in a hidden directory","platform":"linux,macos","sigma_rules":[{"rule_name":"Hidden Files and Directories","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_hidden_files_directories.yml"}],"splunk_rules":[]},{"tech_id":"T1059.004","test_number":2,"atomic_attack_guid":"d0c88567-803d-4dca-99b4-7ce65e7b257c","atomic_attack_name":"Command-Line Interface","platform":"linux,macos","sigma_rules":[{"rule_name":"Suspicious Activity in Shell Commands","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_shell_susp_commands.yml"}],"splunk_rules":[]},{"tech_id":"T1070.003","test_number":1,"atomic_attack_guid":"a934276e-2be5-4a36-93fd-98adbb5bd4fc","atomic_attack_name":"Clear Bash history (rm)","platform":"linux,macos","sigma_rules":[{"rule_name":"Linux Command History Tampering","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_shell_clear_cmd_history.yml"}],"splunk_rules":[]},{"tech_id":"T1070.003","test_number":3,"atomic_attack_guid":"b1251c35-dcd3-4ea1-86da-36d27b54f31f","atomic_attack_name":"Clear Bash history (cat dev/null)","platform":"linux,macos","sigma_rules":[{"rule_name":"Linux Command History Tampering","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_shell_clear_cmd_history.yml"}],"splunk_rules":[]},{"tech_id":"T1070.003","test_number":4,"atomic_attack_guid":"23d348f3-cc5c-4ba9-bd0a-ae09069f0914","atomic_attack_name":"Clear Bash history (ln dev/null)","platform":"linux,macos","sigma_rules":[{"rule_name":"Linux Command History Tampering","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_shell_clear_cmd_history.yml"}],"splunk_rules":[]},{"tech_id":"T1070.003","test_number":5,"atomic_attack_guid":"47966a1d-df4f-4078-af65-db6d9aa20739","atomic_attack_name":"Clear Bash history (truncate)","platform":"linux","sigma_rules":[{"rule_name":"Linux Command History Tampering","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_shell_clear_cmd_history.yml"}],"splunk_rules":[]},{"tech_id":"T1070.003","test_number":6,"atomic_attack_guid":"7e6721df-5f08-4370-9255-f06d8a77af4c","atomic_attack_name":"Clear history of a bunch of shells","platform":"linux,macos","sigma_rules":[{"rule_name":"Linux Command History Tampering","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_shell_clear_cmd_history.yml"}],"splunk_rules":[]},{"tech_id":"T1070.003","test_number":7,"atomic_attack_guid":"784e4011-bd1a-4ecd-a63a-8feb278512e6","atomic_attack_name":"Clear and Disable Bash History Logging","platform":"linux,macos","sigma_rules":[{"rule_name":"Linux Command History Tampering","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_shell_clear_cmd_history.yml"}],"splunk_rules":[]},{"tech_id":"T1105","test_number":1,"atomic_attack_guid":"0fc6e977-cb12-44f6-b263-2824ba917409","atomic_attack_name":"rsync remote file copy (push)","platform":"linux,macos","sigma_rules":[{"rule_name":"Remote File Copy","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_file_copy.yml"}],"splunk_rules":[]},{"tech_id":"T1105","test_number":2,"atomic_attack_guid":"3180f7d5-52c0-4493-9ea0-e3431a84773f","atomic_attack_name":"rsync remote file copy (pull)","platform":"linux,macos","sigma_rules":[{"rule_name":"Remote File Copy","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_file_copy.yml"}],"splunk_rules":[]},{"tech_id":"T1105","test_number":3,"atomic_attack_guid":"83a49600-222b-4866-80a0-37736ad29344","atomic_attack_name":"scp remote file copy (push)","platform":"linux,macos","sigma_rules":[{"rule_name":"Remote File Copy","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_file_copy.yml"}],"splunk_rules":[]},{"tech_id":"T1105","test_number":4,"atomic_attack_guid":"b9d22b9a-9778-4426-abf0-568ea64e9c33","atomic_attack_name":"scp remote file copy (pull)","platform":"linux,macos","sigma_rules":[{"rule_name":"Remote File Copy","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_file_copy.yml"}],"splunk_rules":[]},{"tech_id":"T1105","test_number":6,"atomic_attack_guid":"0139dba1-f391-405e-a4f5-f3989f2c88ef","atomic_attack_name":"sftp remote file copy (pull)","platform":"linux,macos","sigma_rules":[{"rule_name":"Remote File Copy","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_file_copy.yml"}],"splunk_rules":[]},{"tech_id":"T1136.001","test_number":1,"atomic_attack_guid":"40d8eabd-e394-46f6-8785-b9bfa1d011d2","atomic_attack_name":"Create a user account on a Linux system","platform":"linux","sigma_rules":[{"rule_name":"Creation Of An User Account","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/syscall/lnx_auditd_create_account.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Add User Account Type","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_add_user_account_type.yml"}]},{"tech_id":"T1136.001","test_number":6,"atomic_attack_guid":"a1040a30-d28b-4eda-bd99-bb2861a4616c","atomic_attack_name":"Create a new user in Linux with `root` UID and GID.","platform":"linux","sigma_rules":[{"rule_name":"Creation Of An User Account","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/syscall/lnx_auditd_create_account.yml"},{"rule_name":"Privileged User Has Been Created","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_privileged_user_creation.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Add User Account Type","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_add_user_account_type.yml"}]},{"tech_id":"T1057","test_number":1,"atomic_attack_guid":"4ff64f0b-aaf2-4866-b39d-38d9791407cc","atomic_attack_name":"Process Discovery - ps","platform":"linux,macos","sigma_rules":[{"rule_name":"System Info Discovery via Sysinfo Syscall","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/syscall/lnx_auditd_susp_discovery_sysinfo_syscall.yml"}],"splunk_rules":[]},{"tech_id":"T1546.004","test_number":4,"atomic_attack_guid":"694b3cc8-6a78-4d35-9e74-0123d009e94b","atomic_attack_name":"Append to the system shell profile","platform":"linux","sigma_rules":[{"rule_name":"Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_unix_shell_configuration_modification.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_unix_shell_configuration_modification.yml"}]},{"tech_id":"T1546.004","test_number":5,"atomic_attack_guid":"bbdb06bc-bab6-4f5b-8232-ba3fbed51d77","atomic_attack_name":"Append commands user shell profile","platform":"linux","sigma_rules":[{"rule_name":"Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_unix_shell_configuration_modification.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_unix_shell_configuration_modification.yml"}]},{"tech_id":"T1546.004","test_number":6,"atomic_attack_guid":"8fe2ccfd-f079-4c03-b1a9-bd9b362b67d4","atomic_attack_name":"System shell profile scripts","platform":"linux","sigma_rules":[{"rule_name":"Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_unix_shell_configuration_modification.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_unix_shell_configuration_modification.yml"}]},{"tech_id":"T1201","test_number":1,"atomic_attack_guid":"085fe567-ac84-47c7-ac4c-2688ce28265b","atomic_attack_name":"Examine password complexity policy - Ubuntu","platform":"linux","sigma_rules":[{"rule_name":"Password Policy Discovery - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_password_policy_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1201","test_number":2,"atomic_attack_guid":"a7893624-a3d7-4aed-9676-80498f31820f","atomic_attack_name":"Examine password complexity policy - FreeBSD","platform":"linux","sigma_rules":[{"rule_name":"Password Policy Discovery - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_password_policy_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1201","test_number":3,"atomic_attack_guid":"78a12e65-efff-4617-bc01-88f17d71315d","atomic_attack_name":"Examine password complexity policy - CentOS/RHEL 7.x","platform":"linux","sigma_rules":[{"rule_name":"Password Policy Discovery - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_password_policy_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1201","test_number":4,"atomic_attack_guid":"6ce12552-0adb-4f56-89ff-95ce268f6358","atomic_attack_name":"Examine password complexity policy - CentOS/RHEL 6.x","platform":"linux","sigma_rules":[{"rule_name":"Password Policy Discovery - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_password_policy_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1201","test_number":5,"atomic_attack_guid":"7c86c55c-70fa-4a05-83c9-3aa19b145d1a","atomic_attack_name":"Examine password expiration policy - All Linux","platform":"linux","sigma_rules":[{"rule_name":"Password Policy Discovery - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/lnx_auditd_password_policy_discovery.yml"}],"splunk_rules":[]},{"tech_id":"T1685","test_number":1,"atomic_attack_guid":"212cfbcf-4770-4980-bc21-303e37abd0e3","atomic_attack_name":"Auditing Configuration Changes on Linux Host","platform":"linux","sigma_rules":[{"rule_name":"Auditing Configuration Changes on Linux Host","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_auditing_config_change.yml"}],"splunk_rules":[]},{"tech_id":"T1685","test_number":3,"atomic_attack_guid":"7d40bc58-94c7-4fbb-88d9-ebce9fcdb60c","atomic_attack_name":"Logging Configuration Changes on Linux Host","platform":"linux","sigma_rules":[{"rule_name":"Logging Configuration Changes on Linux Host","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_logging_config_change.yml"}],"splunk_rules":[]},{"tech_id":"T1036.003","test_number":2,"atomic_attack_guid":"a315bfff-7a98-403b-b442-2ea1b255e556","atomic_attack_name":"Masquerading as FreeBSD or Linux crond process.","platform":"linux","sigma_rules":[{"rule_name":"Masquerading as Linux Crond Process","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_masquerading_crond.yml"}],"splunk_rules":[]},{"tech_id":"T1053.003","test_number":2,"atomic_attack_guid":"b7d42afa-9086-4c8a-b7b0-8ea3faa6ebb0","atomic_attack_name":"Cron - Add script to all cron subfolders","platform":"macos,linux","sigma_rules":[{"rule_name":"Modifying Crontab","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/cron/lnx_cron_crontab_file_modification.yml"}],"splunk_rules":[]},{"tech_id":"T1105","test_number":5,"atomic_attack_guid":"f564c297-7978-4aa9-b37a-d90477feea4e","atomic_attack_name":"sftp remote file copy (push)","platform":"linux,macos","sigma_rules":[{"rule_name":"Remote File Copy","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_file_copy.yml"}],"splunk_rules":[]},{"tech_id":"T1529","test_number":11,"atomic_attack_guid":"61303105-ff60-427b-999e-efb90b314e41","atomic_attack_name":"Reboot System via `poweroff` - Linux","platform":"linux","sigma_rules":[{"rule_name":"System Shutdown/Reboot - Linux","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/execve/lnx_auditd_system_shutdown_reboot.yml"}],"splunk_rules":[]},{"tech_id":"T1546.004","test_number":2,"atomic_attack_guid":"0a898315-4cfa-4007-bafe-33a4646d115f","atomic_attack_name":"Add command to .bashrc","platform":"macos,linux","sigma_rules":[{"rule_name":"Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_unix_shell_configuration_modification.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_unix_shell_configuration_modification.yml"}]},{"tech_id":"T1546.004","test_number":3,"atomic_attack_guid":"41502021-591a-4649-8b6e-83c9192aff53","atomic_attack_name":"Add command to .shrc","platform":"linux","sigma_rules":[{"rule_name":"Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_unix_shell_configuration_modification.yml"}],"splunk_rules":[{"rule_name":"Linux Auditd Unix Shell Configuration Modification","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_unix_shell_configuration_modification.yml"}]},{"tech_id":"T1574.006","test_number":1,"atomic_attack_guid":"39cb0e67-dd0d-4b74-a74b-c072db7ae991","atomic_attack_name":"Shared Library Injection via /etc/ld.so.preload","platform":"linux","sigma_rules":[{"rule_name":"Modification of ld.so.preload","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/path/lnx_auditd_ld_so_preload_mod.yml"},{"rule_name":"Code Injection by ld.so Preload","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/builtin/lnx_ldso_preload_injection.yml"}],"splunk_rules":[]},{"tech_id":"T1046","test_number":2,"atomic_attack_guid":"515942b0-a09f-4163-a7bb-22fefb6f185f","atomic_attack_name":"Port Scan Nmap","platform":"linux,macos","sigma_rules":[{"rule_name":"Linux Network Service Scanning - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/syscall/lnx_auditd_network_service_scanning.yml"}],"splunk_rules":[]},{"tech_id":"T1046","test_number":12,"atomic_attack_guid":"0d5a2b03-3a26-45e4-96ae-89485b4d1f97","atomic_attack_name":"Port Scan using nmap (Port range)","platform":"linux,macos","sigma_rules":[{"rule_name":"Linux Network Service Scanning - Auditd","rule_link":"https://raw.githubusercontent.com/SigmaHQ/sigma/master/rules/linux/auditd/syscall/lnx_auditd_network_service_scanning.yml"}],"splunk_rules":[]},{"tech_id":"T1016","test_number":3,"atomic_attack_guid":"c141bbdb-7fca-4254-9fd6-f47e79447e17","atomic_attack_name":"System Network Configuration Discovery","platform":"macos,linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd System Network Configuration Discovery","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_system_network_configuration_discovery.yml"}]},{"tech_id":"T1053.002","test_number":2,"atomic_attack_guid":"7266d898-ac82-4ec0-97c7-436075d0d08e","atomic_attack_name":"At - Schedule a job","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd At Application Execution","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_at_application_execution.yml"}]},{"tech_id":"T1083","test_number":4,"atomic_attack_guid":"13c5e1ae-605b-46c4-a79f-db28c77ff24e","atomic_attack_name":"Nix File and Directory Discovery 2","platform":"linux,macos","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd File And Directory Discovery","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_file_and_directory_discovery.yml"}]},{"tech_id":"T1140","test_number":5,"atomic_attack_guid":"b4f6a567-a27a-41e5-b8ef-ac4b4008bb7e","atomic_attack_name":"Base64 decoding with shell utilities","platform":"linux,macos","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Base64 Decode Files","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_base64_decode_files.yml"}]},{"tech_id":"T1140","test_number":9,"atomic_attack_guid":"3a15c372-67c1-4430-ac8e-ec06d641ce4d","atomic_attack_name":"Linux Base64 Encoded Shebang in CLI","platform":"linux,macos","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Base64 Decode Files","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_base64_decode_files.yml"}]},{"tech_id":"T1552.004","test_number":2,"atomic_attack_guid":"46959285-906d-40fa-9437-5a439accd878","atomic_attack_name":"Discover Private SSH Keys","platform":"linux,macos","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Find Ssh Private Keys","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_find_ssh_private_keys.yml"}]},{"tech_id":"T1552.004","test_number":3,"atomic_attack_guid":"7c247dc7-5128-4643-907b-73a76d9135c3","atomic_attack_name":"Copy Private SSH Keys with CP","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Find Ssh Private Keys","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_find_ssh_private_keys.yml"}]},{"tech_id":"T1552.004","test_number":5,"atomic_attack_guid":"864bb0b2-6bb5-489a-b43b-a77b3a16d68a","atomic_attack_name":"Copy Private SSH Keys with rsync","platform":"macos,linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Find Ssh Private Keys","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_find_ssh_private_keys.yml"}]},{"tech_id":"T1552.004","test_number":6,"atomic_attack_guid":"922b1080-0b95-42b0-9585-b9a5ea0af044","atomic_attack_name":"Copy Private SSH Keys with rsync (freebsd)","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Find Ssh Private Keys","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_find_ssh_private_keys.yml"}]},{"tech_id":"T1548.001","test_number":7,"atomic_attack_guid":"db53959c-207d-4000-9e7a-cd8eb417e072","atomic_attack_name":"Make and modify capabilities of a binary","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Setuid Using Setcap Utility","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_setuid_using_setcap_utility.yml"}]},{"tech_id":"T1548.001","test_number":8,"atomic_attack_guid":"1ac3272f-9bcf-443a-9888-4b1d3de785c1","atomic_attack_name":"Provide the SetUID capability to a file","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Setuid Using Setcap Utility","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_setuid_using_setcap_utility.yml"}]},{"tech_id":"T1485","test_number":2,"atomic_attack_guid":"38deee99-fd65-4031-bec8-bfa4f9f26146","atomic_attack_name":"FreeBSD/macOS/Linux - Overwrite file with DD","platform":"linux,macos","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Dd File Overwrite","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_dd_file_overwrite.yml"}]},{"tech_id":"T1003.008","test_number":1,"atomic_attack_guid":"3723ab77-c546-403c-8fb4-bb577033b235","atomic_attack_name":"Access /etc/shadow (Local)","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Possible Access To Credential Files","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_possible_access_to_credential_files.yml"}]},{"tech_id":"T1003.008","test_number":2,"atomic_attack_guid":"5076874f-a8e6-4077-8ace-9e5ab54114a5","atomic_attack_name":"Access /etc/master.passwd (Local)","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Possible Access To Credential Files","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_possible_access_to_credential_files.yml"}]},{"tech_id":"T1003.008","test_number":3,"atomic_attack_guid":"60e860b6-8ae6-49db-ad07-5e73edd88f5d","atomic_attack_name":"Access /etc/passwd (Local)","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Possible Access To Credential Files","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_possible_access_to_credential_files.yml"}]},{"tech_id":"T1053.006","test_number":1,"atomic_attack_guid":"f4983098-bb13-44fb-9b2c-46149961807b","atomic_attack_name":"Create Systemd Service and Timer","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Service Restarted","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_service_restarted.yml"}]},{"tech_id":"T1053.006","test_number":2,"atomic_attack_guid":"3de33f5b-62e5-4e63-a2a0-6fd8808c80ec","atomic_attack_name":"Create a user level transient systemd service and timer","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Service Restarted","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_service_restarted.yml"}]},{"tech_id":"T1053.006","test_number":3,"atomic_attack_guid":"d3eda496-1fc0-49e9-aff5-3bec5da9fa22","atomic_attack_name":"Create a system level transient systemd service and timer","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Service Restarted","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_service_restarted.yml"}]},{"tech_id":"T1548.003","test_number":1,"atomic_attack_guid":"150c3a08-ee6e-48a6-aeaf-3659d24ceb4e","atomic_attack_name":"Sudo usage","platform":"macos,linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Sudo Or Su Execution","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_sudo_or_su_execution.yml"}]},{"tech_id":"T1548.003","test_number":2,"atomic_attack_guid":"2bf9a018-4664-438a-b435-cc6f8c6f71b1","atomic_attack_name":"Sudo usage (freebsd)","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Sudo Or Su Execution","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_sudo_or_su_execution.yml"}]},{"tech_id":"T1548.003","test_number":3,"atomic_attack_guid":"a7b17659-dd5e-46f7-b7d1-e6792c91d0bc","atomic_attack_name":"Unlimited sudo cache timeout","platform":"macos,linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Sudo Or Su Execution","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_sudo_or_su_execution.yml"}]},{"tech_id":"T1548.003","test_number":4,"atomic_attack_guid":"a83ad6e8-6f24-4d7f-8f44-75f8ab742991","atomic_attack_name":"Unlimited sudo cache timeout (freebsd)","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Sudo Or Su Execution","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_sudo_or_su_execution.yml"}]},{"tech_id":"T1548.003","test_number":5,"atomic_attack_guid":"91a60b03-fb75-4d24-a42e-2eb8956e8de1","atomic_attack_name":"Disable tty_tickets for sudo caching","platform":"macos,linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Sudo Or Su Execution","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_sudo_or_su_execution.yml"}]},{"tech_id":"T1548.003","test_number":6,"atomic_attack_guid":"4df6a0fe-2bdd-4be8-8618-a6a19654a57a","atomic_attack_name":"Disable tty_tickets for sudo caching (freebsd)","platform":"linux","sigma_rules":[],"splunk_rules":[{"rule_name":"Linux Auditd Sudo Or Su Execution","rule_link":"https://raw.githubusercontent.com/splunk/security_content/develop/detections/endpoint/linux_auditd_sudo_or_su_execution.yml"}]}] \ No newline at end of file diff --git a/dist/metadata_linux.json b/dist/metadata_linux.json new file mode 100644 index 0000000..dee58d8 --- /dev/null +++ b/dist/metadata_linux.json @@ -0,0 +1,3 @@ +{ + "last_updated": "2026-07-01 22:26:15" +} \ No newline at end of file diff --git a/dist/mitre_layer_combined_linux.json b/dist/mitre_layer_combined_linux.json new file mode 100644 index 0000000..4f65a15 --- /dev/null +++ b/dist/mitre_layer_combined_linux.json @@ -0,0 +1,316 @@ +{ + "name": "ARM - Sigma + Splunk Detection Coverage", + "versions": { + "attack": "19", + "navigator": "5.3.0", + "layer": "4.5" + }, + "domain": "enterprise-attack", + "description": "Sigma OR Splunk coverage", + "filters": { + "platforms": [ + "Linux" + ] + }, + "sorting": 3, + "layout": { + "layout": "side", + "aggregateFunction": "average", + "showID": false, + "showName": true, + "showAggregateScores": false, + "countUnscored": false + }, + "hideDisabled": false, + "techniques": [ + { + "techniqueID": "T1003.008", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1016", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1027.001", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1030", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1033", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1036.003", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1046", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1053.002", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1053.003", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1053.006", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1057", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1059.004", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1070.003", + "score": 100, + "color": "", + "comment": "Tests: 6 | Detected: 6 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1070.006", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1082", + "score": 100, + "color": "", + "comment": "Tests: 8 | Detected: 8 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1083", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1105", + "score": 100, + "color": "", + "comment": "Tests: 6 | Detected: 6 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1136.001", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1140", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1201", + "score": 100, + "color": "", + "comment": "Tests: 5 | Detected: 5 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1222.002", + "score": 100, + "color": "", + "comment": "Tests: 14 | Detected: 14 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1485", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1489", + "score": 100, + "color": "", + "comment": "Tests: 5 | Detected: 5 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1529", + "score": 100, + "color": "", + "comment": "Tests: 10 | Detected: 10 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1543.002", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1546.004", + "score": 100, + "color": "", + "comment": "Tests: 5 | Detected: 5 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1548.001", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1548.003", + "score": 100, + "color": "", + "comment": "Tests: 6 | Detected: 6 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1552.001", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1552.003", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1552.004", + "score": 100, + "color": "", + "comment": "Tests: 4 | Detected: 4 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1560.001", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1564.001", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1574.006", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1685", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + } + ], + "gradient": { + "colors": [ + "#ff6666", + "#ffe766", + "#8ec843" + ], + "minValue": 0, + "maxValue": 100 + } +} \ No newline at end of file diff --git a/dist/mitre_layer_sigma_linux.json b/dist/mitre_layer_sigma_linux.json new file mode 100644 index 0000000..925608e --- /dev/null +++ b/dist/mitre_layer_sigma_linux.json @@ -0,0 +1,316 @@ +{ + "name": "ARM - Sigma Detection Coverage", + "versions": { + "attack": "19", + "navigator": "5.3.0", + "layer": "4.5" + }, + "domain": "enterprise-attack", + "description": "Sigma rule coverage", + "filters": { + "platforms": [ + "Linux" + ] + }, + "sorting": 3, + "layout": { + "layout": "side", + "aggregateFunction": "average", + "showID": false, + "showName": true, + "showAggregateScores": false, + "countUnscored": false + }, + "hideDisabled": false, + "techniques": [ + { + "techniqueID": "T1003.008", + "score": 0, + "color": "", + "comment": "Tests: 3 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1016", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1027.001", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1030", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1033", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1036.003", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1046", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1053.002", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1053.003", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1053.006", + "score": 0, + "color": "", + "comment": "Tests: 3 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1057", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1059.004", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1070.003", + "score": 100, + "color": "", + "comment": "Tests: 6 | Detected: 6 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1070.006", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1082", + "score": 100, + "color": "", + "comment": "Tests: 8 | Detected: 8 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1083", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1105", + "score": 100, + "color": "", + "comment": "Tests: 6 | Detected: 6 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1136.001", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1140", + "score": 0, + "color": "", + "comment": "Tests: 2 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1201", + "score": 100, + "color": "", + "comment": "Tests: 5 | Detected: 5 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1222.002", + "score": 100, + "color": "", + "comment": "Tests: 14 | Detected: 14 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1485", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1489", + "score": 20, + "color": "", + "comment": "Tests: 5 | Detected: 1 | Coverage: %20", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1529", + "score": 100, + "color": "", + "comment": "Tests: 10 | Detected: 10 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1543.002", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1546.004", + "score": 100, + "color": "", + "comment": "Tests: 5 | Detected: 5 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1548.001", + "score": 0, + "color": "", + "comment": "Tests: 2 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1548.003", + "score": 0, + "color": "", + "comment": "Tests: 6 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1552.001", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1552.003", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1552.004", + "score": 0, + "color": "", + "comment": "Tests: 4 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1560.001", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1564.001", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1574.006", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1685", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + } + ], + "gradient": { + "colors": [ + "#ff6666", + "#ffe766", + "#8ec843" + ], + "minValue": 0, + "maxValue": 100 + } +} \ No newline at end of file diff --git a/dist/mitre_layer_splunk_linux.json b/dist/mitre_layer_splunk_linux.json new file mode 100644 index 0000000..fbbb47c --- /dev/null +++ b/dist/mitre_layer_splunk_linux.json @@ -0,0 +1,316 @@ +{ + "name": "ARM - Splunk Detection Coverage", + "versions": { + "attack": "19", + "navigator": "5.3.0", + "layer": "4.5" + }, + "domain": "enterprise-attack", + "description": "Splunk/ESCU rule coverage", + "filters": { + "platforms": [ + "Linux" + ] + }, + "sorting": 3, + "layout": { + "layout": "side", + "aggregateFunction": "average", + "showID": false, + "showName": true, + "showAggregateScores": false, + "countUnscored": false + }, + "hideDisabled": false, + "techniques": [ + { + "techniqueID": "T1003.008", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1016", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1027.001", + "score": 0, + "color": "", + "comment": "Tests: 2 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1030", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1033", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1036.003", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1046", + "score": 0, + "color": "", + "comment": "Tests: 2 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1053.002", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1053.003", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1053.006", + "score": 100, + "color": "", + "comment": "Tests: 3 | Detected: 3 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1057", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1059.004", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1070.003", + "score": 0, + "color": "", + "comment": "Tests: 6 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1070.006", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1082", + "score": 25, + "color": "", + "comment": "Tests: 8 | Detected: 2 | Coverage: %25", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1083", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1105", + "score": 0, + "color": "", + "comment": "Tests: 6 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1136.001", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1140", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1201", + "score": 0, + "color": "", + "comment": "Tests: 5 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1222.002", + "score": 0, + "color": "", + "comment": "Tests: 14 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1485", + "score": 100, + "color": "", + "comment": "Tests: 1 | Detected: 1 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1489", + "score": 100, + "color": "", + "comment": "Tests: 5 | Detected: 5 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1529", + "score": 0, + "color": "", + "comment": "Tests: 10 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1543.002", + "score": 0, + "color": "", + "comment": "Tests: 3 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1546.004", + "score": 100, + "color": "", + "comment": "Tests: 5 | Detected: 5 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1548.001", + "score": 100, + "color": "", + "comment": "Tests: 2 | Detected: 2 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1548.003", + "score": 100, + "color": "", + "comment": "Tests: 6 | Detected: 6 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1552.001", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1552.003", + "score": 0, + "color": "", + "comment": "Tests: 2 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1552.004", + "score": 100, + "color": "", + "comment": "Tests: 4 | Detected: 4 | Coverage: %100", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1560.001", + "score": 0, + "color": "", + "comment": "Tests: 3 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1564.001", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1574.006", + "score": 0, + "color": "", + "comment": "Tests: 1 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + }, + { + "techniqueID": "T1685", + "score": 0, + "color": "", + "comment": "Tests: 3 | Detected: 0 | Coverage: %0", + "enabled": true, + "metadata": [] + } + ], + "gradient": { + "colors": [ + "#ff6666", + "#ffe766", + "#8ec843" + ], + "minValue": 0, + "maxValue": 100 + } +} \ No newline at end of file