Back to skill

Security audit

VMware ESXI-standalone automation skill

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly a coherent vmctl post-install playbook, but it can perform destructive VM cleanup and broad recovery actions without enough scoping or confirmation.

Install only if operators understand that this skill may create, delete, purge, and recover VM state through existing vmctl credentials. Before use, narrow activation to explicit vmctl smoke-test requests, require approval before `delete --force`, `purge`, or `recover --apply`, and verify purge/recovery targets exactly match the intended test VM.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:135
Finding

Ambiguous Tombstone Matching Can Purge Unrelated VM State

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 135–147
Vulnerability Type: Ambiguous resource identification and unsafe destructive operation
Risk Level: Medium

Vulnerable Code

bash
# discover tombstone
python3 - <<'PY'
import glob, os
vm='<name>'
paths=glob.glob('/opt/hermes-vmctl/state/deleted/*.json')
c=[p for p in paths if vm in os.path.basename(p)]
if c:
    c.sort(key=os.path.getmtime, reverse=True)
    print(os.path.basename(c[0])[:-5])
PY

vmctl purge <deleted_name>

Technical Analysis

The tombstone-discovery procedure identifies candidate files by checking whether the original VM name appears anywhere in the tombstone filename:

python
vm in os.path.basename(p)

This is a substring comparison rather than an exact comparison against a canonical VM identifier. If several tombstones contain the same name fragment, the procedure silently selects the most recently modified candidate. It does not parse the JSON record, validate ownership, compare an exact VM name or identifier, or require a unique match before recommending a permanent purge.

For example, a VM named vmctl-test-smoke-123 could match tombstones associated with both vmctl-test-smoke-123 and vmctl-test-smoke-123-copy. Selecting by modification time does not establish that the selected tombstone belongs to the VM deleted during the current smoke test.

The eventual vmctl purge operation is destructive, so using an ambiguously selected identifier creates a resource-confusion vulnerability.

Attack Path

  1. A tombstone for another VM exists under /opt/hermes-vmctl/state/deleted.
  2. Its filename contains the smoke-test VM name as a substring, or the smoke-test name contains an overlapping resource-name fragment.
  3. The unrelated tombstone has a later modification time than the intended tombstone.
  4. The documented Python procedure returns the unrelated tombstone ...[truncated 863 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse each tombstone JSON file and compare its canonical VM name or immutable VM identifier exactly.
  2. Require exactly one matching tombstone. Stop without purging if zero or multiple matches are found.
  3. Correlate the tombstone with the VM created and deleted during the current execution, preferably through an immutable identifier returned by vmctl.
  4. Validate that the selected resource name begins with the required vmctl-test- prefix before purging.
  5. Display the selected tombstone and require operator confirmation when identity cannot be established unambiguously.
  6. Pass the resulting name as a properly quoted argument rather than relying on manual placeholder substitution.

A hardened implementation should follow this pattern:

python
import glob
import json
import os
import sys

expected_name = os.environ["VM_NAME"]
matches = []

for path in glob.glob("/opt/hermes-vmctl/state/deleted/*.json"):
    with open(path, encoding="utf-8") as handle:
        record = json.load(handle)
    if record.get("name") == expected_name:
        matches.append(path)

if len(matches) != 1:
    raise SystemExit(
        f"Expected exactly one tombstone for {expected_name!r}; "
        f"found {len(matches)}"
    )

print(os.path.basename(matches[0])[:-5])

The actual JSON field should be adjusted to the documented vmctl tombstone schema.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:151
Finding

Unscoped Recovery Apply Can Modify Non-Test VM State Without Explicit Authorization

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 151–155
Vulnerability Type: Unscoped state reconciliation exceeding the requested test-resource boundary
Risk Level: Medium

Vulnerable Code

bash
## Recovery flow (if state drift exists)
If ESXi has managed VM but state is missing:

vmctl recover --dry-run
vmctl recover --apply

The same unconditional command pair is also presented in the quick reference at lines 76–79.

Technical Analysis

The Skill is intended for post-install validation with test-only VM names, and it states that non-test resources should not be touched unless the operator explicitly requests it. However, the recovery procedure directs the agent to execute the global command:

bash
vmctl recover --apply

No resource filter, test-name restriction, dry-run output validation, action allowlist, or explicit approval gate is required before applying the recovery plan. If the dry run reports drift involving production or otherwise non-test VMs, following the procedure can reconcile those resources even though the operator requested only a smoke test.

This is a least-privilege and authorization-scope issue: the credentials may legitimately permit global recovery, but the requested task only justifies operations on the newly created test VM.

Attack Path

  1. The environment contains state drift for one or more managed non-test VMs.
  2. An operator requests routine post-install validation or a smoke test.
  3. The agent detects pending, failed, or missing state and follows the documented recovery flow.
  4. vmctl recover --dry-run identifies actions involving non-test resources.
  5. The Skill provides no mandatory review or approval boundary.
  6. The agent runs vmctl recover --apply.
  7. Recovery changes are applied to production or other out-of-scope VM state.

A malicious or compromised managed environment could intentionally present crafted drift so that ...[truncated 782 chars]

Remediation
View remediation

Remediation Suggestions

  1. Treat vmctl recover --dry-run as diagnostic-only by default.
  2. Never run recover --apply automatically during a routine post-install smoke test.
  3. Parse and report every proposed recovery action before execution.
  4. Permit automatic application only when all proposed actions are conclusively associated with the exact test VM created in the current run.
  5. If vmctl supports resource filtering, invoke recovery with the immutable identifier or exact name of the current test VM.
  6. Require explicit operator approval before applying any action involving a non-test resource.
  7. Abort when the recovery plan includes names that do not begin with vmctl-test-, unless the operator has separately authorized each affected resource.
  8. Record the approved recovery plan and verify after application that no additional resources changed.

The procedure should be revised to use an approval gate such as:

text
1. Run `vmctl recover --dry-run`.
2. Report the exact proposed actions.
3. If any action concerns a resource other than the current test VM, stop.
4. Require explicit operator approval before `vmctl recover --apply`.

Where scoped recovery is unavailable, global recover --apply should remain an operator-controlled action outside the automatic smoke-test workflow.

Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger examples include broad phrases like "run a test," "check after install," and "why is it not working," which can match ordinary operator requests and invoke a skill that performs real infrastructure actions, including VM creation and cleanup. Because this skill is operational and not read-only, ambiguous routing increases the chance of unintended execution against ESXi-managed resources.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill documents destructive commands like vmctl delete --force, vmctl purge, and vmctl recover --apply as part of the normal flow without an explicit, prominent warning or confirmation requirement. In this context, those commands can remove VMs and state artifacts, and the broad trigger conditions make accidental invocation more plausible, increasing the risk of unintended deletion or reconciliation changes.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.