Check Parent-Child Process
detection_check_parent_childEvaluate parent-child process relationships to detect suspicious chains. Identifies risks, maps to MITRE techniques, and provides triage notes.
Instructions
Check if a process parent-child relationship is expected or suspicious.
Provide parent and child process filenames (e.g., parent='winword.exe', child='cmd.exe'). Returns whether the relationship is expected, the risk if unexpected, MITRE technique, and triage notes.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| parent | Yes | ||
| child | Yes | ||
| os_filter | No | windows |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- mcp_server/server.py:151-213 (handler)The `detection_check_parent_child` function is the actual tool handler. It takes parent and child process filenames plus an optional OS filter (default 'windows'), normalizes them, looks up the relationship in the parent-child baselines CSV data, and returns whether the relationship is expected, suspicious, or unknown. It supports glob matching via `_match_filename` and wildcard child matching.
def detection_check_parent_child( parent: str, child: str, os_filter: str = "windows", ) -> dict[str, Any]: """Check if a process parent-child relationship is expected or suspicious. Provide parent and child process filenames (e.g., parent='winword.exe', child='cmd.exe'). Returns whether the relationship is expected, the risk if unexpected, MITRE technique, and triage notes. """ parent_lower = parent.lower().strip() child_lower = child.lower().strip() # Strip paths if "\\" in parent_lower or "/" in parent_lower: parent_lower = parent_lower.replace("\\", "/").split("/")[-1] if "\\" in child_lower or "/" in child_lower: child_lower = child_lower.replace("\\", "/").split("/")[-1] matches = [] for row in _get_parent_child(): if row.get("os", "").lower() != os_filter.lower(): continue row_parent = row.get("parent", "").lower() row_child = row.get("child", "").lower() # Check parent match (exact or glob) parent_match = _match_filename(row_parent, parent_lower) # Check child match (exact, glob, or wildcard) child_match = row_child == "*" or _match_filename(row_child, child_lower) if parent_match and child_match: matches.append({ "parent": row.get("parent", ""), "child": row.get("child", ""), "os": row.get("os", ""), "expected": row.get("expected", ""), "risk_if_unexpected": row.get("risk_if_unexpected", ""), "mitre_id": row.get("mitre_id", ""), "context": row.get("context", ""), "notes": row.get("notes", ""), }) if not matches: return { "found": False, "parent": parent_lower, "child": child_lower, "os": os_filter, "assessment": ( "No baseline entry found. This may be suspicious — " "undocumented parent-child relationships warrant investigation." ), "suggestion": ( "Try checking each process individually with " "detection_lookup_binary to see if either is a known LOLBin." ), } # Return most specific match (prefer exact child over wildcard) best = sorted(matches, key=lambda m: (m["child"] == "*", m["expected"] == "true")) return {"found": True, "matches": best} - mcp_server/server.py:142-155 (schema)The `@mcp.tool` decorator on `detection_check_parent_child` registers the tool with FastMCP, providing annotations (title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint) that serve as the tool's schema/metadata.
@mcp.tool( annotations={ "title": "Check Parent-Child Process", "readOnlyHint": True, "destructiveHint": False, "idempotentHint": True, "openWorldHint": False, }, ) def detection_check_parent_child( parent: str, child: str, os_filter: str = "windows", ) -> dict[str, Any]: - mcp_server/server.py:142-155 (registration)The `@mcp.tool()` decorator on line 142 registers `detection_check_parent_child` as an MCP tool with the FastMCP server instance.
@mcp.tool( annotations={ "title": "Check Parent-Child Process", "readOnlyHint": True, "destructiveHint": False, "idempotentHint": True, "openWorldHint": False, }, ) def detection_check_parent_child( parent: str, child: str, os_filter: str = "windows", ) -> dict[str, Any]: - mcp_server/server.py:43-45 (helper)The `_match_filename` helper function provides glob-based filename matching (case-insensitive via `fnmatch`), used by the handler to match parent and child process names against CSV baseline entries.
def _match_filename(pattern: str, value: str) -> bool: """Match with glob support (e.g., 'tomcat*.exe').""" return fnmatch(value.lower(), pattern.lower()) - mcp_server/server.py:61-65 (helper)The `_get_parent_child` helper function lazily loads and caches the parent-child baselines CSV data (`parent_child_baselines.csv`) used by the handler for lookups.
def _get_parent_child() -> list[dict[str, str]]: global _parent_child_cache if _parent_child_cache is None: _parent_child_cache = _load_csv("parent_child_baselines.csv") return _parent_child_cache