Back to skill

Security audit

SSH Vault

Security checks for vulnerabilities and agentic risk

Overview

The skill is transparent about remote SSH execution, but it asks for overly broad vault access and stores reusable session state insecurely.

Install only if you are comfortable giving this agent remote command capability through your SSH vault. Before use, restrict registration to specific hosts, avoid the wildcard request, pin the Docker image by digest, bind the service to a private interface unless intentionally exposed, and store session data in a user-private file with restrictive permissions.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T08 · Insecure Dependencies

Error
Location
SKILL.md:20
Finding
Mutable Third-Party Container Image Used for Security-Sensitive Vault Service<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 20-24 **Vulnerability Type**: Unpinned third-party container dependency **Risk Level**: High ### Vulnerable Code ```bash docker run -d -p 3001:3001 \ -v vault-data:/app/data \ -v vault-config:/app/config \ qsobad/ssh-vault-mcp:latest ``` ### Technical Analysis The documented deployment command executes the mutable third-party image `qsobad/ssh-vault-mcp:latest`. The `latest` tag is not cryptographically bound to the version reviewed with this Skill and can be changed by the publisher or anyone who compromises the publisher's registry account. This container occupies a particularly sensitive trust boundary because it receives persistent vault storage and manages SSH credentials, authorization sessions, and remote command execution. Publishing port 3001 without an explicit loopback address can also expose the service on every host network interface, depending on the Docker configuration and host firewall. The behavior is not necessary in its current form. The declared functionality can be provided using an image pinned to a reviewed immutable digest and with narrower network exposure. ### Attack Path 1. The container publisher's account, build environment, or image registry is compromised, or the mutable `latest` tag is intentionally replaced. 2. The attacker publishes a modified image under `qsobad/ssh-vault-mcp:latest`. 3. A user follows the Skill's documented Docker setup or later pulls the tag again. 4. Docker executes the changed image and attaches the persistent `/app/data` and `/app/config` volumes. 5. The altered service reads or modifies vault data, captures credentials or approval information, and abuses the service's ability to execute commands on managed SSH hosts. ### Impact Assessment A compromised image could obtain access to persistent vault data and configuration, capture credentials submitted during approval, tamper with authorization workflows, or execute com ...[truncated 247 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace the mutable tag with a reviewed, immutable image digest, for example `repository@sha256:<verified-digest>`. - Document the exact application version, source repository, build provenance, and digest verification procedure. - Use signed images and verify signatures or attestations before deployment. - Bind the published port to loopback by default, such as `127.0.0.1:3001:3001`, unless remote exposure is explicitly required. - Apply container hardening, including a non-root user, dropped Linux capabilities, a read-only root filesystem where practical, resource limits, and narrowly scoped volume permissions. - Establish a controlled upgrade process that reviews and pins every new image digest before deployment. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/vault.mjs:124
Finding
Agent Registration Requests Access to Every Vault-Managed Host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/vault.mjs`, lines 124-129 **Vulnerability Type**: Excessive authorization scope **Risk Level**: High ### Vulnerable Code ```js } else if (cmd === 'register') { const r = await api('POST', '/api/agent/request-access', { name: 'openclaw', publicKey: PUBLIC_KEY_B64, requestedHosts: ['*'], }); console.log(JSON.stringify(r, null, 2)); ``` ### Technical Analysis The `register` command unconditionally requests the wildcard host scope `['*']`. It does not accept a user-selected allowlist, infer the single host needed for the current task, or require separate confirmation before requesting global access. The Skill's declared purpose is to execute commands on user-selected vault-managed hosts. Access to every managed host is therefore not inherently required. Requesting wildcard access violates least-privilege principles and increases the consequences of private-key theft, session theft, malicious command input, or accidental agent behavior. The vault may still require a human or administrator to approve this request. That approval boundary reduces automatic exploitability but does not eliminate the excessive request made by the client. ### Attack Path 1. The user or agent invokes `node scripts/vault.mjs register`. 2. The script submits an access request containing `requestedHosts: ['*']`. 3. A vault administrator approves the request, potentially relying on the Skill's registration workflow without recognizing that it requests every host. 4. The agent receives authorization covering all hosts allowed by the wildcard. 5. An attacker who later obtains the agent private key or a valid session, or who can cause the agent to issue commands, enumerates the available hosts and submits remote commands across the entire approved scope. ### Impact Assessment Successful exploitation could provide command-submission capability against every host included by the vault's wildcard authorization r ...[truncated 424 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Change registration to require an explicit host allowlist supplied by the user. - Default to requesting no hosts rather than using a wildcard. - Reject `*` unless the user separately provides explicit confirmation for global access. - Display the exact requested host scope before submitting the registration request. - Encourage one agent identity per narrowly defined environment or host group. - Support later, auditable scope expansion instead of requesting maximum privileges during initial registration. - Ensure the vault approval interface prominently identifies wildcard requests and requires enhanced confirmation for them. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/vault.mjs:24
Finding
Vault Session Token Stored Unsafely in a Predictable Shared Temporary File<![CDATA[ ## Vulnerability Details **File Location**: `scripts/vault.mjs`, lines 24 and 52-65 **Vulnerability Type**: Unsafe temporary-file handling and sensitive session exposure **Risk Level**: High ### Vulnerable Code ```js const SESSION_FILE = '/tmp/ssh-vault-session.json'; ``` ```js function loadSession() { try { if (existsSync(SESSION_FILE)) { const data = JSON.parse(readFileSync(SESSION_FILE, 'utf-8')); if (data.expiresAt && data.expiresAt > Date.now()) return data; } } catch {} return null; } function saveSession(session) { writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2)); } ``` ### Technical Analysis The program stores the vault `sessionId` in the fixed, predictable path `/tmp/ssh-vault-session.json`. The shared temporary directory is normally accessible to multiple local users. The write operation does not specify restrictive file permissions, verify ownership, use exclusive creation, or prevent symbolic-link traversal. The resulting permissions depend on the process umask and may allow another local user to read the session data. In addition, `writeFileSync` follows an existing symbolic link. A local attacker may pre-create the expected path as a symbolic link to another file writable by the victim, causing the victim process to overwrite that target when an approved session is saved. The fixed path also causes session collisions among users or concurrent instances. Because the cached identifier is subsequently attached to privileged vault API requests, it must be treated as sensitive authentication material. ### Attack Path **Session disclosure path:** 1. A user completes the vault approval flow. 2. `saveSession` writes the returned `sessionId` and expiration time to `/tmp/ssh-vault-session.json`. 3. The file is created under an insufficiently restrictive umask or is otherwise accessible to another local user. 4. The local attacker reads the active session identifier. 5. The attacker attempts to reu ...[truncated 1285 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Store session state in a user-specific runtime directory such as `$XDG_RUNTIME_DIR`, after validating that the directory is owned by the current user and has mode `0700`. - If no secure runtime directory exists, create a private directory with mode `0700` outside the shared `/tmp` namespace. - Create the session file atomically with mode `0600`. - Use no-follow and exclusive-creation protections where supported, and verify file ownership and type before reading or replacing it. - Write to a securely created temporary file in the same private directory and atomically rename it into place. - Separate session files by vault origin and agent identity to avoid cross-instance collisions. - Delete expired session files rather than merely ignoring their contents. - Avoid printing the full session identifier in the `session` and `check-unlock` command output unless explicitly required; redact it by default. - Ensure the server binds every session to the registered public key and continues to require a valid request signature. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Ae1

High
Category
analysis-evasion
Content
node scripts/vault.mjs exec <host> <command> [timeout]
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/vault.mjs exec <host> <command> [timeout]
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/vault.mjs exec <host> <command> [timeout]
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/vault.mjs exec <host> <command> [timeout]
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/vault.mjs exec <host> <command> [timeout]
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/vault.mjs exec <host> <command> [timeout]
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/vault.mjs exec <host> <command> [timeout]
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill includes a registration path that asks the vault for broad agent access rather than limiting permissions to specific intended hosts or contexts. In a tool whose purpose is remote command execution, overbroad registration materially increases blast radius: if registration succeeds, the agent could be authorized far beyond what the manifest and user likely expect.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
Using requestedHosts: ['*'] explicitly seeks unrestricted authorization to all hosts managed by the vault. That is dangerous because any compromise, misuse, or mistaken invocation of this agent could pivot into fleet-wide command execution instead of access to only the hosts needed for the current task.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill clearly requires access to environment variables and networked command execution against a vault service, but it declares no explicit tool scope or permission boundaries. That omission makes the skill harder to sandbox and review, increasing the chance that an agent can access secrets or perform network actions beyond what users expect.

Rp1

Medium
Category
MCP Rug Pull
Confidence
87% confidence
Finding
The documentation instructs users to run a container from a mutable image reference using the latest tag. This creates a supply-chain risk because future pulls may fetch unexpected or compromised code, and users cannot reproduce or verify the exact image version they deployed.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill enables arbitrary remote SSH command execution, explicitly allows shell metacharacters, and describes session reuse, yet it does not prominently warn that these actions can change or damage remote systems. In this context, missing warnings and guardrails are dangerous because the skill is intended for high-impact operations on real infrastructure.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
data: {"status":"executing"}
   data: {"status":"completed","stdout":"...","stderr":"...","exitCode":0,"sessionId":"..."}
   ```
4. Save `sessionId` to `/tmp/ssh-vault-session.json` — subsequent commands skip approval

Shell metacharacters (`&&`, `;`, `|`, `$()`, backticks) are all allowed in commands.
Confidence
95% confidence
Finding
The documented behavior caches a session ID so subsequent commands can bypass the approval flow, enabling autonomous remote command execution after a single authorization. In a skill whose core function is arbitrary SSH access, this materially increases the risk of lateral movement, unintended repeated actions, and abuse if the agent or session store is compromised.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The skill is described as executing commands on existing vault-managed hosts, but the documentation also includes host-management functionality through the vault API. This expands the operational scope from execution to infrastructure enrollment, increasing the chance an agent can alter the trusted host set or stage access to additional systems.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
Including host registration/onboarding in a skill whose stated purpose is command execution violates least privilege and introduces a stronger persistence and expansion vector. If abused, an agent could request or facilitate enrollment of new hosts and credentials, extending its reach beyond approved systems.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The code stores session data in /tmp/ssh-vault-session.json without setting restrictive file permissions or using a more secure credential store. On multi-user systems or shared environments, this can expose active session identifiers to other local users or processes, enabling unauthorized use of the vault session until expiration.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
Hardcoding the registration name as 'openclaw' conflicts with the manifest statement that the skill is not for openclaw nodes. This mismatch can cause operators to misclassify the agent, accidentally grant inappropriate trust, or bind the skill to environments it was explicitly not meant to target.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "ISC",
  "type": "commonjs",
  "dependencies": {
    "libsodium-wrappers": "^0.8.2"
  }
}
Confidence
96% confidence
Finding
The dependency is specified with a caret range, which allows installation of newer matching versions over time instead of a single fixed artifact. In a security-sensitive skill that signs SSH vault API requests using cryptographic libraries, this increases supply-chain risk because a compromised or breaking upstream release could be pulled into future installs without explicit review.

Missing User Warnings

Low
Confidence
72% confidence
Finding
The exec command sends a host and command to the vault API for remote execution, which is a safety-relevant subprocess-like operation on external systems. Although remote execution is part of the command's stated purpose, the implementation does not provide any runtime confirmation or warning that the command will be executed on the specified host.