T05 · Unauthorized Access and Privilege Escalation
- Location
lib/mandated_mcp_bridge.py:548- Finding
Configurable MCP Subprocess Inherits the Entire Parent Environment
- Content
View full analysis
None: argv = shlex.split(self._config.mandated_mcp_command) if not argv: raise MandatedVaultMcpUnavailableError( "Mandated-vault MCP command is empty." ) env = os.environ.copy() env["ERC_MANDATED_CONTRACT_VERSION"] = self._config.mandated_contract_version if self._config.mandated_chain_id is not None: env["ERC_MANDATED_CHAIN_ID"] = str(self._config.mandated_chain_id) _apply_default_rpc_env(env, self._config) try: self._process = await asyncio.create_subprocess_exec( *argv, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, limit=1024 * 1024, env=env, ) ``` ### Technical Analysis The MCP launcher command is configurable through `ERC_MANDATED_MCP_COMMAND`, but the launched executable receives a copy of the complete parent-process environment through `os.environ.copy()`. The inherited environment can contain sensitive values used elsewhere by the Skill, including: - `PREDICT_PRIVATE_KEY` - `PREDICT_PRIVY_PRIVATE_KEY` - `ERC_MANDATED_AUTHORITY_PRIVATE_KEY` - `ERC_MANDATED_EXECUTOR_PRIVATE_KEY` - `PREDICT_API_KEY` - `OPENROUTER_API_KEY` - Unrelated secrets injected into the OpenClaw process by the host The subprocess only requires a limited collection of MCP and chain configuration values. Giving it every parent environment variable violates least privilege. Although `create_subprocess_exec` avoids shell interpretation, it does not prevent a malicious, compromised, or incorrectly selected executable from reading and disclosing inherited secrets. The command is parsed with `shlex.split`, so this is not a demonstrated ...[truncated 1502 chars]- Remediation
View remediation
