Back to skill

Security audit

Vai Layman88

Security checks for vulnerabilities and agentic risk

Overview

This skill appears purpose-built for decentralized identity, but it handles private keys and authentication proofs in ways that deserve review before installation.

Install only if you are comfortable with this skill managing agent identity keys on the local machine. Set a strong `BILLIONS_NETWORK_MASTER_KMS_KEY` before creating or importing identities, protect `$HOME/.openclaw/billions` as sensitive secret storage, avoid passing real private keys or tokens on the command line, and verify every signing/linking request before allowing the agent to run it.

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/storage/keys.js:50
Finding
Private keys are stored in plaintext when no master key is configured<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:50-65` and duplicated implementation at `skills/verified-agent-identity/scripts/shared/storage/keys.js:50-65` **Vulnerability Type**: Plaintext storage of cryptographic private keys **Risk Level**: High ### Vulnerable Code ```js _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 }, }; } ``` ### Technical Analysis The key-storage implementation treats encryption as optional. If `BILLIONS_NETWORK_MASTER_KMS_KEY` is absent, whitespace-only, or shorter than the accepted minimum, `_encodeEntry` serializes `privateKeyHex` directly into `kms.json`. Although the Skill documentation warns that keys may be stored in plaintext, documentation does not mitigate the exposure. Private keys are identity authentication credentials and should not be written unencrypted to persistent storage by default. This behavior violates secure secret-storage and fail-closed principles. The encryption key is also derived by applying a single SHA-256 operation to the environment variable rather than using a password-based key derivation function. A low-entropy configured value would consequently be more susceptible to offline guessing if an encrypted key file were obtained. ### Attack Path 1. A user creates or imports an identity without setting a valid `BILLIONS_NETWORK_MASTER_KMS_KEY`. 2. The Skill stores the private key under the `provider: "plain"` format in `~/.openclaw/billions/kms.json`. 3. An attacker obtains read access through another local account, malware, an exposed backup, an overly broad support bundle, or accidental file disclosure. 4. The attacker extracts `privateKeyHex`. 5. The attac ...[truncated 666 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Fail closed when no valid encryption key or secure key-store provider is available; do not create or import an identity in plaintext mode. 2. Store private keys in an operating-system keychain, hardware-backed key store, HSM, or dedicated secrets manager where possible. 3. If file encryption must be supported, use a memory-hard password KDF such as Argon2id or scrypt with a unique random salt and documented minimum entropy requirements. 4. Retain authenticated encryption such as AES-256-GCM, but version the complete KDF and cipher parameters in the stored record. 5. Add a migration command that detects `provider: "plain"` and securely rewrites existing entries after encryption is configured. 6. Warn users without printing private-key material, and require explicit migration or key rotation for previously exposed plaintext keys. 7. Apply restrictive filesystem permissions in addition to encryption. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/shared/storage/base.js:25
Finding
Sensitive identity files are written without explicitly restrictive permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/base.js:25-31` and duplicated implementation at `skills/verified-agent-identity/scripts/shared/storage/base.js:25-31` **Vulnerability Type**: Insecure filesystem permissions for sensitive data **Risk Level**: Medium ### Vulnerable Code ```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); } ``` The directory is also created without an explicit owner-only mode: ```js async ensureDirectory() { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); } ``` ### Technical Analysis The shared storage class writes `kms.json`, challenge records, identity metadata, credentials, profiles, and DID records. Neither directory creation nor temporary-file creation supplies an explicit permission mode. Access therefore depends on the process umask and pre-existing directory permissions. On a host with a permissive umask, the temporary file or final file may be readable by other local users. The temporary file is particularly relevant because it contains the complete serialized secret before being renamed. Renaming it does not correct its mode. The implementation also does not verify whether the storage directory or target path has been replaced with an unsafe symbolic link or is owned by another user. The primary confirmed weakness, however, is the lack of enforced owner-only permissions. ### Attack Path 1. The Skill runs on a multi-user system with a permissive umask or within a pre-existing storage directory with broad permissions. 2. `ensureDirectory` creates or uses `~/.openclaw/billions` without checking that it is owner-only. 3. `writeFile` creates `kms.json.tmp` using default filesystem permissions and renames it to `kms.json`. 4. Another local user or process reads the ...[truncated 876 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create `~/.openclaw/billions` with mode `0700`. 2. Create temporary and final sensitive files with mode `0600`, independently of the process umask. 3. After writing, verify and, where safe, repair the permissions of existing files. 4. Open temporary files with exclusive creation semantics to prevent replacement or collisions. 5. Verify that the storage directory and target files are owned by the current user and are not symbolic links. 6. Keep the atomic temporary-file-and-rename pattern, but generate unpredictable temporary names in the protected directory. 7. Add startup checks that reject unsafe ownership or permissions rather than silently continuing. 8. Add automated tests that run under permissive umasks and confirm that secrets remain owner-readable only. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verifySignature.js:18
Finding
Verification challenges do not expire and are not consumed after successful use<![CDATA[ ## Vulnerability Details **File Location**: `scripts/verifySignature.js:18-58`, with challenge generation and storage in `scripts/generateChallenge.js:17-23` and `scripts/shared/storage/challenge.js:8-23`; duplicated equivalents exist under `skills/verified-agent-identity/scripts/` **Vulnerability Type**: Replayable authentication challenge **Risk Level**: Medium ### Vulnerable Code Challenge generation and persistent storage: ```js // Generate random challenge const challenge = randomInt(0, 10000000000).toString(); // Save challenge to storage await challengeStorage.save(args.did, challenge); outputSuccess(challenge); ``` A timestamp is stored, but no expiration policy is enforced: ```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); } ``` Verification retrieves the challenge, compares it, and returns success without deleting or rotating it: ```js // 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 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: c ...[truncated 2300 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate challenges using at least 128 bits from a cryptographically secure random source, such as `crypto.randomBytes(32)`. 2. Store a normalized creation and expiration timestamp for every challenge. 3. Reject expired challenges using a short, documented validity period. 4. Atomically consume or delete the challenge immediately after successful verification. 5. Ensure concurrent verification attempts cannot both consume the same challenge; use locking, transactional storage, or an atomic compare-and-delete operation. 6. Bind each challenge to the intended DID, verifier, audience, session, and operation so that it cannot be reused across contexts. 7. Avoid passing authentication tokens directly through command-line arguments where process listings or shell history may expose them; support standard input or a protected file descriptor. 8. Add tests for expiration, replay after success, concurrent replay, DID mismatch, and purpose/audience mismatch. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (52)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description emphasizes decentralized identity workflows, but the documentation also reveals local private-key custody, optional plaintext key storage, and raw key management behavior. This mismatch is dangerous because users and orchestrators may approve or auto-invoke the skill as an identity helper without realizing it also handles highly sensitive key material and credential stores.

Missing User Warnings

High
Confidence
97% confidence
Finding
The documentation states that private keys may be stored in plaintext when the master KMS key is not configured, but this warning appears deep in the security section rather than prominently before identity creation. That creates a realistic risk that operators will generate identities and persist secrets insecurely on disk without understanding the exposure.

Credential Access

High
Category
Privilege Escalation
Content
- `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
Confidence
88% confidence
Finding
The skill explicitly accesses credential and identity storage files, including verifiable credentials and key material, which are highly sensitive assets in an agent environment. Even if intended for legitimate identity workflows, exposing a skill with broad access to these stores raises the risk of credential disclosure, misuse, or unintended exfiltration through subsequent tool actions.

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 flagged for memory disclosure and memory-exhaustion denial-of-service issues in WebSocket handling. In an agent skill that may communicate with remote services, any reachable WebSocket surface increases risk because malformed or fragmented frames from a remote peer could crash the process or expose memory contents.

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
88% confidence
Finding
brace-expansion 2.0.2 has multiple denial-of-service issues caused by pathological expansion patterns that can cause extreme CPU or memory consumption. Even though this is often a build/tooling dependency, it is still a real vulnerability if any runtime path or auxiliary tooling processes attacker-controlled glob-like strings.

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 is reported vulnerable to multiple URL parsing and canonicalization issues, including host confusion and SSRF-relevant edge cases. This is particularly relevant for an identity-oriented skill that may fetch remote schemas, DID documents, or attestations, because parser inconsistencies can let attackers bypass allowlists or redirect requests to unintended internal targets.

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
92% confidence
Finding
ws 7.5.10 is flagged for a memory exhaustion DoS via fragmented frames and tiny chunks. If the affected WebSocket server/client code is reachable through RPC or network-facing components, a remote attacker may be able to degrade availability or crash the process with crafted traffic.

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
87% confidence
Finding
underscore 1.13.6 is flagged for uncontrolled recursion in functions such as flatten and isEqual, which can cause denial of service on maliciously nested inputs. If any dependency applies these helpers to untrusted request or response data, a remote attacker may trigger excessive recursion and process instability.

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 associated with multiple HTTP parsing, smuggling, and request/response confusion issues. This is especially concerning in a decentralized identity skill that likely performs outbound HTTP fetches for DID documents, registries, schemas, or attestations, because parser discrepancies can enable SSRF, cache poisoning, or cross-request contamination.

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. Because this project includes multiple network and RPC-related packages, any reachable WebSocket handling exposed to untrusted peers materially increases the chance of remote availability impact or data leakage.

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
75% confidence
Finding
The code persists credential material to a predictable local file (credentials.json), which can expose sensitive identity artifacts if the host is multi-user, compromised, or the file permissions are weak. In an agent identity skill, stored credentials may enable impersonation, privacy loss, or unauthorized proof generation, making local credential persistence more sensitive than ordinary application state.

Missing User Warnings

High
Confidence
98% confidence
Finding
When no master key is available, the code intentionally falls back to storing raw private key material on disk in plaintext. In a skill that manages decentralized identity and authentication keys, this creates a direct secret-at-rest exposure: any local user, malware, backup system, or accidental file disclosure can recover the agent's private keys and impersonate identities or forge authentication proofs.

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
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
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

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
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
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

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 pins ws 8.18.0 under @ethersproject/providers, and the reported advisories include uninitialized memory disclosure and memory-exhaustion DoS in WebSocket handling. In an identity/agent-authentication skill that may maintain network connections to blockchain or verifier infrastructure, vulnerable WebSocket code increases exposure to remote attacks that can crash the process or leak process memory.

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.

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.

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.

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.

Missing User Warnings

High
Confidence
98% confidence
Finding
This code explicitly falls back to storing private keys with provider: "plain" when no master key is available, which means sensitive cryptographic material can be persisted to disk unencrypted. In an agent identity skill, those keys are high-value secrets: compromise of the local filesystem, backups, logs, or container volumes could allow an attacker to steal the keys and impersonate the agent or misuse attestations.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding
The README instructs users to run `npx clawhub@latest install verified-agent-identity`, which fetches and executes the latest package version at install time. Using a moving target for an installation/bootstrap tool weakens supply-chain integrity because a compromised or malicious future release could be executed automatically by users or agents.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding
This second installation example repeats the same unsafe pattern of invoking `npx clawhub@latest`, which downloads and runs unpinned code. In an agent-skill context, users may copy-paste the command directly, increasing exposure to supply-chain compromise if the upstream package changes or is hijacked.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The documentation explicitly tells users to pass an Ethereum private key on the command line via `--key <your-ethereum-private-key>`. Secrets supplied as CLI arguments are commonly exposed via shell history, process listings, audit logs, terminal scrollback, and orchestration tooling, making this a direct credential-handling weakness for highly sensitive key material.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill requires Node, uses an optional environment secret, and explicitly performs network-backed identity and attestation operations, but it does not declare an explicit tool/permission scope. In an agent environment, missing scope declarations can cause the skill to be invoked with broader-than-expected access and make review and containment harder.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The invocation examples are broad enough to match common identity-related requests and instruct the agent to perform sensitive identity-linking actions with minimal exclusion conditions. In practice, that can lead to over-eager triggering of signing/linking flows in response to loosely phrased user prompts, increasing the chance of unauthorized proof generation or identity association.

Static analysis

No suspicious patterns detected.