Back to skill

Security audit

Agent

Security checks for vulnerabilities and agentic risk

Overview

This identity skill is coherent and not malicious, but it needs Review because it can persist agent private keys in plaintext and has weak authentication-state handling.

Install only if you are comfortable with this skill managing a persistent agent identity. Set `BILLIONS_NETWORK_MASTER_KMS_KEY` before creating or importing any identity, avoid passing private keys directly on the command line, restrict access to `$HOME/.openclaw/billions`, and treat signed challenge tokens as reusable until the implementation adds expiration and consumption.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/shared/storage/keys.js:45
Finding

Private keys may be stored in plaintext without restrictive filesystem permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/shared/storage/keys.js:45-61 and scripts/shared/storage/base.js:25-31
Vulnerability Type: Plaintext storage of cryptographic private keys and insufficient file-permission enforcement
Risk Level: High

The same vulnerable implementation is duplicated under skills/verified-agent-identity/scripts/shared/storage/keys.js and skills/verified-agent-identity/scripts/shared/storage/base.js.

Vulnerable code in scripts/shared/storage/keys.js:

javascript
_encodeEntry({ alias, privateKeyHex, createdAt }) {
  const masterKey = getMasterKey();
  if (masterKey) {
    return {
      version: 1,
      provider: "encrypted",
      data: { alias, key: encryptKey(privateKeyHex, masterKey), createdAt },
    };
  }
  return {
    version: 1,
    provider: "plain",
    data: { alias, key: privateKeyHex, createdAt },
  };
}

Related storage code in scripts/shared/storage/base.js:

javascript
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);
}

Technical Analysis

If BILLIONS_NETWORK_MASTER_KMS_KEY is absent, invalid, or shorter than the required minimum, _encodeEntry deliberately writes the raw privateKeyHex value to $HOME/.openclaw/billions/kms.json.

FileStorage does not explicitly set restrictive permissions on either the containing directory or the temporary and final files. Their effective permissions therefore depend on the process umask and pre-existing filesystem state. In environments with a permissive umask, kms.json can be readable by other local users. An existing directory or file with overly broad permissions is also not corrected.

The temporary path is predictable (kms.json.tmp) and is created without an e ...[truncated 1985 chars]

Remediation
View remediation

Remediation Suggestions

  1. Make encrypted key storage mandatory. Refuse identity creation or key import when no valid master key or secure OS keystore is available; do not silently fall back to plaintext.
  2. Prefer an operating-system credential store, hardware-backed keystore, or dedicated KMS rather than an environment-variable-derived file-encryption key.
  3. If passphrases are supported, replace a single SHA-256 derivation with a password KDF such as Argon2id or scrypt using a unique random salt and suitable cost parameters.
  4. Create $HOME/.openclaw/billions with mode 0700 and verify that it is owned by the current user and is not a symbolic link.
  5. Create temporary files with mode 0600, an unpredictable name, and exclusive creation. Atomically rename them only after flushing the data.
  6. Explicitly enforce mode 0600 on kms.json after creation and migration, regardless of umask.
  7. Reject symbolic links and unsafe pre-existing paths using appropriate lstat, ownership, and file-type checks.
  8. Warn users and require explicit confirmation before importing a private key through command-line arguments, because command-line values may be exposed through shell history or process inspection.
  9. Apply the same corrections to the duplicated files under skills/verified-agent-identity/.
  10. Add automated tests that verify plaintext fallback is impossible and that directory, temporary-file, and final-file permissions are owner-only.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verifySignature.js:17
Finding

Verified authentication challenges remain reusable indefinitely

Content
View full analysis

Vulnerability Details

File Location: scripts/verifySignature.js:17-57 and scripts/shared/storage/challenge.js:14-25
Vulnerability Type: Authentication replay caused by missing challenge expiration and consumption
Risk Level: Medium

The same vulnerable implementation is duplicated under skills/verified-agent-identity/scripts/verifySignature.js and skills/verified-agent-identity/scripts/shared/storage/challenge.js.

Vulnerable verification code in scripts/verifySignature.js:

javascript
// Get the stored challenge
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);
}

// Create DID resolver that fetches from remote resolver
const resolveDIDDocument = {
  resolve: async (did) => {
    const resp = await fetch(
      `https://resolver.privado.id/1.0/identifiers/${did}`,
    );
    const didResolutionRes = await resp.json();
    return didResolutionRes;
  },
};

// Create JWS packer and unpack token
const jws = new JWSPacker(kms, resolveDIDDocument);
const basicMessage = await jws.unpack(byteEncoder.encode(args.token));

// Verify the sender
if (basicMessage.from !== args.did) {
  console.error(
    `Error: Invalid from: expected from ${args.did}, got ${basicMessage.from}`,
  );
  process.exit(1);
}

// Verify the challenge matches
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");

Challenge persistence in scripts/shared/storage/challenge.js:

javascript
async save(did, challenge) {
  const entries = await this.readFile();
  const created_at
...[truncated 2461 chars]
Remediation
View remediation

Remediation Suggestions

  1. Store a cryptographically random nonce together with an explicit expiration timestamp and consumed status.
  2. Reject challenges older than a short, documented lifetime, such as five minutes.
  3. Atomically consume or delete the challenge immediately after successful verification.
  4. Ensure verification and consumption occur as one atomic operation so concurrent requests cannot both authenticate with the same token.
  5. Bind the challenge to the intended verifier, operation, audience, and session in addition to the DID.
  6. Validate relevant JWS metadata, including issuance and expiration times when available, expected message type, audience, and protocol context.
  7. Store only a hash of the nonce where feasible and compare it using a timing-safe operation.
  8. Avoid recording complete tokens in logs and advise callers not to place sensitive bearer-like proofs in persistent command histories.
  9. Apply the same changes to the duplicated implementation under skills/verified-agent-identity/.
  10. Add tests confirming that expired challenges fail, a successful token cannot be reused, and concurrent replay attempts result in no more than one success.
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
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (49)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill is presented as an identity-verification and attestation utility, but its documented behavior also includes local key management and storage of private keys, potentially in plaintext when no master KMS key is configured. That mismatch can mislead operators and agent orchestrators into approving a skill with more sensitive capabilities than its description suggests, increasing the risk of secret exposure and unsafe deployment.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

The skill documents storage of highly sensitive materials including private keys and verifiable credentials under a predictable local path, with an explicit note that keys may be stored in plaintext if a master key is not set. In an agent environment, that creates a valuable credential target for any other tool, plugin, user session, or compromise able to read the filesystem, enabling account takeover, impersonation, and correlation of identities.

Content

Scanner excerpt · SKILL.md (reported line 172)May include surrounding context.

md
- `kms.json` - **CRITICAL**: Contains private keys (encrypted if BILLIONS_NETWORK_MASTER_KMS_KEY is set, otherwise in plaintext)
- `defaultDid.json` - DID identifiers and public keys
- `challenges.json` - Authentication challenges history
- `credentials.json` - Verifiable credentials
- `identities.json` - Identity metadata
- `profiles.json` - Profile data

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
95% confidence
Finding

ws 8.18.0 is a known vulnerable WebSocket library version with advisories for uninitialized memory disclosure and memory-exhaustion denial of service. In an agent identity skill that may communicate with remote services or wallets, a WebSocket stack flaw is materially relevant because remote peers could potentially trigger crashes, resource exhaustion, or data leakage.

Content

No source excerpt is available for this finding.

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
89% confidence
Finding

brace-expansion 2.0.2 is associated with multiple denial-of-service issues caused by pathological expansion behavior and memory exhaustion. Although often a transitive utility dependency, its presence is still a valid risk because attacker-controlled patterns processed anywhere in the dependency chain can hang the process or consume excessive resources.

Content

No source excerpt is available for this finding.

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
93% confidence
Finding

fast-uri 3.1.0 has multiple host confusion and SSRF-related advisories tied to malformed URL parsing and normalization edge cases. That is particularly dangerous in an identity/proof ecosystem that may fetch remote DID documents, schemas, registries, or JSON-LD contexts, because parser confusion can bypass host allowlists or redirect backend requests to unintended internal targets.

Content

No source excerpt is available for this finding.

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
94% confidence
Finding

ws 7.5.10 is flagged for a memory-exhaustion DoS issue from fragmented frames and small data chunks. If any transitive component opens WebSocket connections to untrusted peers, an attacker may be able to degrade or crash the agent process, which is meaningful for always-on agent infrastructure.

Content

No source excerpt is available for this finding.

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 functions like _.flatten and _.isEqual, which can lead to stack exhaustion or denial of service on crafted inputs. Because identity and proof objects may be attacker-supplied and deeply nested, this dependency is more concerning than it would be in a purely local-only utility context.

Content

No source excerpt is available for this finding.

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 affected by numerous high-severity HTTP parsing and request-smuggling style issues, including response queue poisoning and CRLF injection variants. This is especially relevant for an agent identity skill that may make outbound HTTP requests to attestations, registries, DID resolvers, or context endpoints, since malformed remote interactions could subvert request routing, poison responses, or enable SSRF-like effects.

Content

No source excerpt is available for this finding.

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
95% confidence
Finding

ws 8.17.1 is flagged for both uninitialized memory disclosure and memory exhaustion DoS. Given that identity-agent stacks often rely on persistent network connections and untrusted remote peers, these flaws are operationally significant and can affect confidentiality and availability.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

This code explicitly falls back to storing private keys with provider "plain" when no master key is available, which writes raw private key material to disk. For an identity/authentication skill handling agent credentials, plaintext-at-rest storage materially increases the risk of account takeover if the filesystem, backups, logs, containers, or developer workstations are accessed.

Content

No source excerpt is available for this finding.

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
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · SKILL.md (reported line 10)May include surrounding context.

nk agents to human identities using Billions ERC-8004 and Attestation Registries. Verify and generate authentication proofs. Based on iden3 self-sovereign identity protocol. metadata: { "category": "identity", "clawdbot": { "requires": { "bins": ["node"] }, "config": { "optionalEnv": ["BILLIONS_NETWORK_MASTER_KMS_KEY"] } } } homepage: https://billions.network/

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 to sign a challenge.
  3. When you need to link a human to the agent's DID.
  4. When you need to verify a signature to confirm identity ownership.
  5. When you use shared JWT tokens for authentication.
  6. When you need to create and manage decentralized identities.

After installing the plugin run the following commands to create an identity and link it to your human DID:

bash
cd scripts && npm inst

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
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · skills/verified-agent-identity/SKILL.md (reported line 10)May include surrounding context.

nk agents to human identities using Billions ERC-8004 and Attestation Registries. Verify and generate authentication proofs. Based on iden3 self-sovereign identity protocol. metadata: { "category": "identity", "clawdbot": { "requires": { "bins": ["node"] }, "config": { "optionalEnv": ["BILLIONS_NETWORK_MASTER_KMS_KEY"] } } } homepage: https://billions.network/

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 to sign a challenge.
  3. When you need to link a human to the agent's DID.
  4. When you need to verify a signature to confirm identity ownership.
  5. When you use shared JWT tokens for authentication.
  6. When you need to create and manage decentralized identities.

After installing the plugin run the following commands to create an identity and link it to your human DID:

bash
cd scripts && npm inst

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill explicitly instructs users to pass a private key via a command-line argument, which is unsafe because command-line arguments are commonly exposed through shell history, process listings, audit logs, and agent execution traces. This is especially dangerous in an agent environment where tool invocations may be persisted or observable by other components, turning a one-time setup step into long-term key compromise.

Content

No source excerpt is available for this finding.

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
98% confidence
Finding

The lockfile includes ws 8.18.0, which is flagged for uninitialized memory disclosure and memory-exhaustion denial of service. In an identity/authentication skill that may open websocket connections through blockchain or RPC libraries, these issues are more concerning because network-facing parsing of attacker-controlled websocket traffic can expose process memory or allow service disruption.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 97)May include surrounding context.

md
function newDataStorage(ethStateStorage) {
  return {
    credential: new CredentialStorage(
      new IdentitiesFileStorage("credentials.json"),
    ),
    identity: new IdentityStorage(
      new IdentitiesFileStorage("identities.json"),

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/shared/bootstrap.js (reported line 54)May include surrounding context.

js
function newDataStorage(ethStateStorage) {
  return {
    credential: new CredentialStorage(
      new IdentitiesFileStorage("credentials.json"),
    ),
    identity: new IdentityStorage(
      new IdentitiesFileStorage("identities.json"),

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/verified-agent-identity/README.md (reported line 97)May include surrounding context.

md
function newDataStorage(ethStateStorage) {
  return {
    credential: new CredentialStorage(
      new IdentitiesFileStorage("credentials.json"),
    ),
    identity: new IdentityStorage(
      new IdentitiesFileStorage("identities.json"),

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/verified-agent-identity/SKILL.md (reported line 172)May include surrounding context.

md
function newDataStorage(ethStateStorage) {
  return {
    credential: new CredentialStorage(
      new IdentitiesFileStorage("credentials.json"),
    ),
    identity: new IdentityStorage(
      new IdentitiesFileStorage("identities.json"),

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/verified-agent-identity/scripts/shared/bootstrap.js (reported line 54)May include surrounding context.

js
function newDataStorage(ethStateStorage) {
  return {
    credential: new CredentialStorage(
      new IdentitiesFileStorage("credentials.json"),
    ),
    identity: new IdentityStorage(
      new IdentitiesFileStorage("identities.json"),

Rp1

Medium
Category
MCP Rug Pull
Confidence
87% confidence
Finding

The README instructs users to run npx clawhub@latest install ..., which fetches and executes the latest published package rather than a pinned, reviewed version. If the package or its distribution channel is compromised, users could execute attacker-controlled code during installation.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
87% confidence
Finding

This installation command again relies on npx clawhub@latest, causing remote code execution from an unpinned package version at install time. In security-sensitive identity tooling, this increases supply-chain risk because the installer could access local files, secrets, or generated keys.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill declares execution requirements and clearly relies on network access and environment-provided key material, but it does not declare an explicit tool/permission scope. In an agent setting, missing scope declarations increase the chance that the skill is granted broader capabilities than reviewers or runtime policy expect, especially because it handles identities, signatures, and local secret storage.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill instructs users to pass a private key directly on the command line, which commonly exposes secrets through shell history, process listings, audit logs, CI output, and agent traces. Because this skill manages persistent identities, disclosure of that key would allow full impersonation of the agent identity and unauthorized signing.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script writes a newly created DID and public key to persistent storage via didsStorage.save(...), but there is no confirmation prompt, user-facing disclosure before the write, or inline warning that local state will be modified. Although identity creation is the script's purpose, the persistence side effect is not explicitly disclosed in this file before it occurs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The KMS is backed by KeysFileStorage("kms.json"), which means private key material is persisted to a local file. Storing cryptographic secrets unencrypted on disk increases exposure to local compromise, accidental inclusion in backups or repositories, and theft by other processes or users on the host.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.