Back to skill

Security audit

simple-skill-ilya

Security checks for vulnerabilities and agentic risk

Overview

This identity skill mostly matches its stated purpose, but it stores private keys in plaintext and has an unsafe message-sending helper that can execute unintended shell commands.

Review before installing. Do not use this skill with valuable or reusable private keys unless the key storage is hardened. Treat --to and challenge inputs as untrusted; the message-sending command should be fixed to avoid shell interpolation before use. Run it only in an isolated account or environment and update the vulnerable dependency tree.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/shared/utils.js:108
Finding
Shell Command Injection Through the Message Recipient<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/utils.js:108-111`, invoked from `scripts/signChallenge.js:82` and `scripts/linkHumanToAgent.js:126` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code ```js function sendDirectMessage(target, message) { const { execSync } = require("child_process"); execSync(`openclaw message send --target ${target} --message "${message}"`); } ``` The vulnerable function is called with command-line data: ```js sendDirectMessage(args.to, codeFormating(tokenString)); ``` ```js sendDirectMessage(args.to, urlFormating(verificationMessage, url)); ``` ### Technical Analysis `sendDirectMessage()` constructs a shell command by directly interpolating the dynamic `target` and `message` values into a string passed to `child_process.execSync()`. The `target` value originates from the `--to` command-line argument and is not validated, escaped, or safely quoted. Because `execSync()` executes through a shell, an attacker can include shell control characters such as semicolons, pipes, redirections, command substitutions, or logical operators in the recipient value. The `message` value is also interpolated into a double-quoted shell string. If dynamic message content contains shell-significant syntax, it may provide an additional injection surface. Shell invocation is not necessary for the declared functionality. The Skill only needs to invoke the `openclaw` executable with a fixed set of arguments, so this implementation exceeds the minimum execution privilege required. ### Attack Path 1. An attacker supplies or influences the sender identifier used as the `--to` argument. 2. The Agent runs either `signChallenge.js` or `linkHumanToAgent.js` with that attacker-controlled value. 3. The script passes `args.to` to `sendDirectMessage()`. 4. `sendDirectMessage()` concatenates the value into a shell command. 5. Shell metacharacters terminate or modify the intended `openclaw` c ...[truncated 1015 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Eliminate shell-string construction. Invoke the executable directly with a structured argument array: ```js function sendDirectMessage(target, message) { const { execFileSync } = require("child_process"); execFileSync( "openclaw", ["message", "send", "--target", target, "--message", message], { stdio: "inherit", shell: false, }, ); } ``` Additional hardening should include: 1. Validate `target` against the exact documented identifier format using an allowlist expression. 2. Apply reasonable length limits to both `target` and `message`. 3. Reject control characters, null bytes, and unexpected line breaks. 4. Do not attempt to fix the issue with ad hoc shell escaping; avoid the shell entirely. 5. Add automated tests using values containing semicolons, quotes, command substitution, pipes, and redirection operators. 6. Run the Skill under a restricted account with no unnecessary filesystem or system privileges. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/shared/storage/base.js:25
Finding
Private Keys Persisted in Plaintext Without Enforced File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:13-25` and `scripts/shared/storage/base.js:25-32` **Vulnerability Type**: Insecure storage of cryptographic key material and unsafe temporary-file permissions **Risk Level**: High ### Vulnerable Code The key store serializes raw private keys: ```js async importKey(args) { const keys = await this.readFile(); const index = keys.findIndex((entry) => entry.alias === args.alias); if (index >= 0) { keys[index].privateKeyHex = args.key; } else { keys.push({ alias: args.alias, privateKeyHex: args.key }); } await this.writeFile(keys); } ``` The generic storage implementation writes that data without explicit restrictive permissions: ```js async writeFile(data) { await this.ensureDirectory(); const json = JSON.stringify(data, null, 2); const tempPath = `${this.filePath}.tmp`; await fs.writeFile(tempPath, json, "utf-8"); await fs.rename(tempPath, this.filePath); } ``` Directory creation also omits an explicit mode: ```js async ensureDirectory() { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); } ``` ### Technical Analysis `KeysFileStorage` stores each private key directly in JSON as `privateKeyHex`. The resulting `kms.json` file is not encrypted. The storage layer does not explicitly create the identity directory with mode `0700` or the key file with mode `0600`. Actual access therefore depends on the process umask and pre-existing directory permissions. The temporary file, `kms.json.tmp`, also contains the complete plaintext key and is created without an explicit secure mode. The documentation acknowledges that `kms.json` contains unencrypted private keys, but disclosure does not mitigate the security risk. Long-term raw-key persistence without enforced access controls is unsafe for identity-signing functionality. ### Attack Path 1. The Agent creates or imports an Ethereum private key. 2. `KeysFileStora ...[truncated 1227 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Prefer an operating-system keychain, encrypted keystore, hardware-backed key manager, or dedicated KMS rather than plaintext JSON storage. If file storage must remain supported: 1. Create `$HOME/.openclaw/billions` with mode `0700`. 2. Create temporary and final key files with mode `0600`. 3. Verify and correct permissions on existing directories and files before reading or writing keys. 4. Open files with exclusive and no-follow semantics where supported to reduce symlink and replacement attacks. 5. Use unique temporary files in the same protected directory rather than a predictable `.tmp` filename. 6. Flush the temporary file before atomic replacement where durability is required. 7. Ensure cleanup removes temporary files after failures. 8. Encrypt private keys at rest using an authenticated encryption scheme and a key obtained from a secure credential store. 9. Avoid accepting private keys directly on the command line because command-line values may appear in process listings or shell history. 10. Document key rotation and recovery procedures for installations that used the plaintext format. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verifySignature.js:17
Finding
Signature Verification Challenges Can Be Replayed Indefinitely<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/challenge.js:8-21,25-28` and `scripts/verifySignature.js:17-22,48-58` **Vulnerability Type**: Missing challenge expiration and single-use enforcement **Risk Level**: Medium ### Vulnerable Code Challenge creation records a timestamp but does not define an expiration: ```js async save(did, challenge) { const entries = await this.readFile(); const created_at = new Date(); const index = entries.findIndex((entry) => entry.did === did); if (index >= 0) { // Update existing entry entries[index] = { did, challenge, created_at }; } else { // Add new entry entries.push({ did, challenge, created_at }); } await this.writeFile(entries); } ``` Challenge retrieval returns only the value and discards the timestamp: ```js async getChallenge(did) { const entry = await this.find(did); return entry?.challenge; } ``` Verification checks equality but neither validates age nor consumes the challenge: ```js const challenge = await challengeStorage.getChallenge(args.did); if (!challenge) { console.error(`Error: No challenge found for DID: ${args.did}`); console.error("Generate a challenge first with generateChallenge.js"); process.exit(1); } ``` ```js const payload = basicMessage.body; if (payload.message !== challenge) { console.error( `Error: Invalid signature: challenge mismatch ${payload.message} !== ${challenge}`, ); process.exit(1); } outputSuccess("Signature verified successfully"); ``` ### Technical Analysis Challenge-response authentication requires freshness. A challenge should normally have a short validity period and become unusable immediately after successful verification. Although the storage record includes `created_at`, `verifySignature.js` never evaluates it. The successful verification path also does not call `challengeStorage.delete()` or otherwise mark the record as consumed. Consequently, the same valid signed token remai ...[truncated 1626 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Implement expiring, single-use challenges: 1. Generate challenges with at least 128 bits of cryptographically secure randomness, such as `crypto.randomBytes(32)`. 2. Store a creation time and explicit expiration time. 3. Reject challenges older than a short, documented validity window. 4. Atomically mark or delete the challenge after successful verification. 5. Prevent concurrent verification requests from consuming the same challenge more than once. 6. Bind each challenge to the expected DID, verifier, purpose, audience, and session identifier. 7. Store only a cryptographic hash of the challenge when practical. 8. Periodically remove expired records. 9. Add replay tests that verify a token fails on its second use and after expiration. Consumption must be atomic with the verification decision. A simple read followed by an independent delete may still permit two concurrent requests to validate the same token. ]]>
Vulnerability Patterns
  • 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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (34)

YARA rule 'agent_skill_prompt_injection_hidden_instructions': Prompt injection or hidden instructions embedded in AI agent skill text [agent_skills]

High
Category
YARA Match
Content
on and identity management tools for agents. Link, proof, sign, and verify.
metadata: { "category": "identity" }
---

# Initial Setup (Run after installation)

Before using this skill for the first time, you **must** execute:

```bash
cd scripts && npm install
node createNewEthereumIdentity.js
node linkHumanToAgent.js --to <sender> --challenge <challenge_value>
```

# When to use this Skill

Lets AI agents create and manage their own identities on the Billions Network, and link those identities to a human owner.

1. When you need to link your agent identity to an owner.
2. When you need sign a challenge.
3. When you need link a human to the agent's DID.
4. When you need to verify a signature to confirm identity ownership.
5. When use shared JWT tokens for authentication.
6. When you need to create and manage decentralized identities.

## Scope

All identity data is stored in `$HOME/.openclaw/billions` for compatibility with the OpenClaw plugin.

# Scripts:

### createNewEthereumIdentit
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Credential Access

High
Category
Privilege Escalation
Content
- `kms.json` - **CRITICAL**: Contains unencrypted private keys
- `defaultDid.json` - DID identifiers and public keys
- `challenges.json` - Authentication challenges history
- `credentials.json` - Verifiable credentials
- `identities.json` - Identity metadata
- `profiles.json` - Profile data
Confidence
92% confidence
Finding
The skill explicitly documents storage of sensitive identity artifacts, including unencrypted private keys in kms.json and verifiable credentials in local files. In the context of an agent skill with filesystem and network-adjacent behavior, this creates a real credential exposure risk if the directory is accessed by other tools, users, logs, backups, or compromised processes.

Known Vulnerable Dependency: ws==8.18.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
94% confidence
Finding
ws 8.18.0 is present and is flagged for memory disclosure and memory-exhaustion denial of service issues. In an agent identity/authentication skill that may maintain websocket-based RPC or verifier connections, a vulnerable ws version can expose process memory or allow remote service disruption via crafted frames.

Known Vulnerable Dependency: brace-expansion==2.0.2 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
84% confidence
Finding
brace-expansion 2.0.2 is present and has multiple reported DoS issues involving pathological expansion patterns that can consume excessive CPU or memory. While often build- or tooling-adjacent, any runtime path that accepts attacker-influenced glob-like patterns could be abused to hang the process.

Known Vulnerable Dependency: fast-uri==3.1.0 — 7 advisory(ies): CVE-2026-13676 (fast-uri vulnerable to host confusion via failed IDN canonicalization); CVE-2026-18446 (fast-uri vulnerable to host confusion via backslash authority introducer); CVE-2026-75975 (fast-uri vulnerable to server-side request forgery via malformed IPv6 normalizat) +4 more

High
Category
Supply Chain
Confidence
95% confidence
Finding
fast-uri 3.1.0 is reported vulnerable to multiple URI parsing and canonicalization flaws, including host confusion and SSRF-relevant edge cases. In an identity/authentication skill that may resolve remote resources, DIDs, schemas, or verifier endpoints, malformed URI handling can meaningfully expand attack surface and make this dependency more dangerous than in a purely local tool.

Known Vulnerable Dependency: ws==7.5.10 — 1 advisory(ies): CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
93% confidence
Finding
ws 7.5.10 is flagged for memory-exhaustion DoS and appears in an older transitive chain. If any websocket listener/client path processes attacker-controlled fragmented messages, an attacker may be able to degrade or crash the service.

Known Vulnerable Dependency: jsonpath==1.2.1 — 1 advisory(ies): CVE-2026-1615 (jsonpath has Arbitrary Code Injection via Unsafe Evaluation of JSON Path Express)

High
Category
Supply Chain
Confidence
96% confidence
Finding
jsonpath 1.2.1 is reported vulnerable to arbitrary code injection via unsafe evaluation of JSONPath expressions. Even as a transitive dependency, this is high risk if any code path evaluates attacker-controlled JSONPath input, because it can potentially lead to code execution inside the agent process.

Known Vulnerable Dependency: minimatch==5.1.6 — 3 advisory(ies): CVE-2026-27904 (minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regu); CVE-2026-26996 (minimatch has a ReDoS via repeated wildcards with non-matching literal in patter); CVE-2026-27903 (minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adja)

High
Category
Supply Chain
Confidence
90% confidence
Finding
minimatch 5.1.6 has several ReDoS issues caused by crafted glob patterns leading to catastrophic backtracking or combinatorial work. If any user-supplied patterns reach this library, an attacker can cause CPU exhaustion and deny service.

Known Vulnerable Dependency: underscore==1.13.6 — 1 advisory(ies): CVE-2026-27601 (Underscore has unlimited recursion in _.flatten and _.isEqual, potential for DoS)

High
Category
Supply Chain
Confidence
90% confidence
Finding
underscore 1.13.6 is reported to allow unlimited recursion in flatten/equality helpers, enabling denial of service with deeply recursive inputs. In agent ecosystems that may process arbitrary credential or RPC JSON, this can be abused to crash or stall the process if such helpers are invoked on attacker-controlled data.

Known Vulnerable Dependency: undici==5.29.0 — 12 advisory(ies): CVE-2026-1525 (Undici has an HTTP Request/Response Smuggling issue); CVE-2026-6733 (undici vulnerable to HTTP response queue poisoning via keep-alive socket reuse); CVE-2026-1527 (Undici has CRLF Injection in undici via `upgrade` option) +9 more

High
Category
Supply Chain
Confidence
96% confidence
Finding
undici 5.29.0 is reported vulnerable to multiple serious HTTP parsing and request-handling flaws, including request/response smuggling, queue poisoning, and CRLF injection scenarios. Because this skill revolves around remote identity and proof interactions, outbound HTTP is likely central, making a vulnerable HTTP client especially concerning for SSRF, cache poisoning, or cross-request contamination risks.

Known Vulnerable Dependency: ws==8.17.1 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
94% confidence
Finding
ws 8.17.1 is another vulnerable websocket implementation instance affected by memory disclosure and fragmentation-based memory exhaustion. Multiple vulnerable websocket versions in one lockfile increase the chance that at least one network-facing path remains exploitable.

Credential Access

High
Category
Privilege Escalation
Content
function newDataStorage(ethStateStorage) {
  return {
    credential: new CredentialStorage(
      new IdentitiesFileStorage("credentials.json"),
    ),
    identity: new IdentityStorage(
      new IdentitiesFileStorage("identities.json"),
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The utility module exposes a direct messaging helper that spawns a shell command with unsanitized `target` and `message` values interpolated into the command string. This creates a command injection risk and also introduces an unrelated outbound messaging capability in a shared utility file, which is especially dangerous in an agent skill because attacker-controlled inputs could trigger arbitrary command execution or covert message sending.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill exposes capabilities that imply environment access and network communication, but it declares no explicit tool scope or permissions boundary. In an agent setting, this makes identity creation, message sending, and access to sensitive local state less auditable and easier to invoke unintentionally or with excessive privilege.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The setup flow instructs immediate execution of scripts that create persistent identity material and then perform a linking action, without upfront consent language or a warning that unencrypted private keys will be stored locally and linkage data may be transmitted to another party. This can cause operators or agents to create sensitive credentials and disclose identity associations before understanding the security consequences.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script sends a direct message to an arbitrary recipient supplied via the `--to` argument without any confirmation, preview, or user-facing disclosure of the outbound action. In an agent context, this can enable unintended message delivery, phishing-style identity linking prompts, or covert outbound communication if another component invokes the script with attacker-controlled parameters.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code persists private cryptographic key material directly to a local JSON file as plaintext (`privateKeyHex`) with no encryption, access-control enforcement, or user warning. If the filesystem is readable by other local users, included in backups, exposed via logs, or accidentally committed, an attacker can recover the private keys and fully impersonate the agent or sign arbitrary data.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The code sends messages via a subprocess without any visible warning, consent, logging, or disclosure, making outbound communication easy to hide from users or reviewers. In an agent identity skill, undisclosed messaging is more suspicious because it is unrelated to core DID/authentication helpers and could be abused for covert data exfiltration or unauthorized actions.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The script sends the generated signed token to the recipient specified by `--to` via `sendDirectMessage`, which is a network/message transmission of user-derived data. While argument validation and error output are present, there is no visible confirmation prompt, user-facing notice about the outbound transmission, or explanatory comment/docstring describing this behavior in the file.

Known Vulnerable Dependency: uuid==13.0.0 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
88% confidence
Finding
The lockfile pins uuid 13.0.0, which is reported vulnerable to a missing bounds check when v3/v5/v6 APIs are called with a caller-provided buffer. Even though this is only a dependency manifest and not proof of reachable exploitation, the package version is present and therefore represents a real supply-chain risk if affected APIs are used anywhere in the skill or its transitive libraries.

Known Vulnerable Dependency: uuid==9.0.1 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
87% confidence
Finding
uuid 9.0.1 is also present transitively and carries the same bounds-check issue for certain namespace/version APIs when a buffer is provided. This is a genuine dependency risk, though likely lower severity unless the affected API pattern is exercised by the skill or upstream libraries.

Known Vulnerable Dependency: ajv==8.17.1 — 1 advisory(ies): CVE-2025-69873 (ajv has ReDoS when using `$data` option)

Low
Category
Supply Chain
Confidence
82% confidence
Finding
ajv 8.17.1 is flagged for ReDoS when the $data option is enabled and attacker-controlled schemas or inputs are processed. In an identity stack that may validate credential or proof payloads, this can become a request-level denial of service if unsafe ajv features are enabled by the application or a library.

Known Vulnerable Dependency: elliptic==6.6.1 — 1 advisory(ies): CVE-2025-14505 (Elliptic Uses a Cryptographic Primitive with a Risky Implementation)

Low
Category
Supply Chain
Confidence
78% confidence
Finding
elliptic 6.6.1 is flagged for use of a risky cryptographic implementation. In a package dealing with identity, signing, and proofs, dependence on weaker or problematic crypto implementations increases assurance risk even if immediate exploitability is not evident from the lockfile alone.

Known Vulnerable Dependency: uuid==8.3.2 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
87% confidence
Finding
uuid 8.3.2 is another vulnerable transitive copy affected by the same bounds-check flaw. Multiple vulnerable copies increase maintenance risk and make it more likely at least one reachable code path remains unpatched.

Known Vulnerable Dependency: stream-json==1.9.1 — 1 advisory(ies): CVE-2026-71429 (stream-json: pick/ignore/filter/replace filters are O(depth²) on nested input — )

Low
Category
Supply Chain
Confidence
80% confidence
Finding
stream-json 1.9.1 is flagged for quadratic behavior on deeply nested input for certain filters. This is a real algorithmic complexity issue, but impact depends on whether the affected filters are used on attacker-controlled JSON payloads.