Back to skill

Security audit

Zoho CRM MCP

Security checks across malware telemetry and agentic risk

Overview

The skill is a coherent Zoho CRM integration, but its helper scripts run shell commands with a credential-bearing environment variable in a way that can become command injection.

Review before installing. Use read-only Zoho scopes first, do not echo or share the full MCP URL, treat it like a password, and avoid running the bundled Python scripts unless the shell-injection issue is fixed by calling mcporter without bash -c and by validating ZOHO_MCP_URL.

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (12)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        cmd = ["bash", "-c", f'mcporter call "{MCP_URL}.{TOOL}" --args "$(< {tmp_path})"']
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
Confidence
95% confidence
Finding
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        full_cmd = f'mcporter call "{MCP_URL}.{tool}" --args "$(< {tmp_path})"'
        result = subprocess.run(["bash", "-c", full_cmd], capture_output=True, text=True, timeout=30)
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
Confidence
98% confidence
Finding
result = subprocess.run(["bash", "-c", full_cmd], capture_output=True, text=True, timeout=30)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        cmd = ["bash", "-c", f'mcporter call "{MCP_URL}.{tool}" --args "$(< {tmp_path})"']
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
Confidence
96% confidence
Finding
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

Tainted flow: 'cmd' from os.environ.get (line 50, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
try:
        cmd = ["bash", "-c", f'mcporter call "{MCP_URL}.{TOOL}" --args "$(< {tmp_path})"']
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
Confidence
98% confidence
Finding
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

Tainted flow: 'full_cmd' from os.environ.get (line 72, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
try:
        full_cmd = f'mcporter call "{MCP_URL}.{tool}" --args "$(< {tmp_path})"'
        result = subprocess.run(["bash", "-c", full_cmd], capture_output=True, text=True, timeout=30)
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
Confidence
99% confidence
Finding
result = subprocess.run(["bash", "-c", full_cmd], capture_output=True, text=True, timeout=30)

Tainted flow: 'cmd' from os.environ.get (line 37, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
try:
        cmd = ["bash", "-c", f'mcporter call "{MCP_URL}.{tool}" --args "$(< {tmp_path})"']
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
Confidence
98% confidence
Finding
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill clearly instructs users to use shell commands and environment variables, but it does not declare corresponding permissions or capability requirements. This can undermine trust and reviewability because an agent or operator may not realize the skill expects command execution and access to sensitive environment data such as the MCP endpoint URL.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill recommends enabling write-capable CRM actions such as create, update, upsert, notes, events, and tags, but does not prominently warn that these operations can modify live production CRM data. In an agent context, this raises the risk of unintended or automated changes to customer records, notes, or scheduling data.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documentation shows an MCP endpoint format containing an embedded token and instructs users to export and echo it without any credential-handling warning. Even though the value is presented as an example, this pattern normalizes exposing a bearer-like secret in shell history, logs, screenshots, or terminal output, which could allow unauthorized CRM access if a real token is disclosed.

Missing User Warnings

Medium
Confidence
80% confidence
Finding
The script writes request payloads containing CRM query criteria to a persistent temp file under `/tmp` using `delete=False`. Although Python temp files are typically created with restrictive permissions, the data can remain on disk if the process crashes before cleanup, exposing potentially sensitive CRM search terms or query content to local compromise or forensic recovery.

Unvalidated Output Injection

High
Category
Output Handling
Content
try:
        full_cmd = f'mcporter call "{MCP_URL}.{tool}" --args "$(< {tmp_path})"'
        result = subprocess.run(["bash", "-c", full_cmd], capture_output=True, text=True, timeout=30)
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
Confidence
98% confidence
Finding
subprocess.run(["bash", "-c", full_cmd], capture_output

Unvalidated Output Injection

High
Category
Output Handling
Content
try:
        cmd = ["bash", "-c", f'mcporter call "{MCP_URL}.{tool}" --args "$(< {tmp_path})"']
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
Confidence
90% confidence
Finding
subprocess.run(cmd, capture_output

VirusTotal

VirusTotal findings are pending for this skill version.

View on VirusTotal

Static analysis

No suspicious patterns detected.