Back to skill

Security audit

Web3Dropper Verified Agent

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent identity tool, but it handles long-lived private keys and verification flows with protections that users should review before installing.

Install only if you are comfortable with this agent creating or importing identity keys, storing them unencrypted under $HOME/.openclaw/billions, using external Billions/PolygonID services, and sending signed artifacts through openclaw messages. Protect that directory like a wallet, avoid passing real private keys on the command line, and prefer a patched version that enforces 0700/0600 permissions, removes CLI key import, pins installation paths, updates dependencies, and expires or consumes verification challenges.

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:13
Finding
Unencrypted Private Keys Are Stored Without Explicit Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:13-23`; `scripts/shared/storage/base.js:24-30` **Vulnerability Type**: Plaintext sensitive-data storage and unsafe temporary-file permissions **Risk Level**: High ### Vulnerable Code `scripts/shared/storage/keys.js:13-23`: ```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); } ``` `scripts/shared/storage/base.js:24-30`: ```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); } ``` ### Technical Analysis The key store serializes private keys directly into JSON and writes them to `$HOME/.openclaw/billions/kms.json`. The temporary file and final file are not created with an explicit `0600` mode, while the containing directory is not explicitly created with a `0700` mode. Protection therefore depends on the process umask and any permissions already present on the directory or files. The temporary file contains the same plaintext private keys as the final file. If a write or rename fails, that temporary file may remain on disk. No operating-system keychain, encrypted keystore, hardware-backed key store, or application-level encryption protects the key material. Although private-key storage is necessary for the declared signing functionality, plaintext storage without enforced access controls exceeds the minimum safe privilege model for long-lived identity credentials. ### Attack Path 1. A user creates or imports an identity through the Skill. 2. The Skill writes the identity private key into `kms.json.tmp` and renames it to `kms.json`. ...[truncated 1031 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store long-lived private keys in an operating-system keychain, hardware security module, hardware wallet, or encrypted keystore rather than plaintext JSON. 2. Create `$HOME/.openclaw/billions` with mode `0700` and verify that an existing directory is not group- or world-accessible. 3. Create key files and temporary files with mode `0600`, for example by passing `{ encoding: "utf-8", mode: 0o600 }` when creating them. 4. Check and correct the permissions of existing `kms.json` files before reading or updating them. 5. Use unique, unpredictable temporary filenames and ensure they are removed in a `finally` block after failures. 6. Consider authenticated encryption with a key obtained from a secure prompt or platform keychain if a dedicated secure storage provider is unavailable. 7. Avoid including private-key values in logs, thrown errors, backups, or diagnostic output. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/createNewEthereumIdentity.js:25
Finding
Existing Private Keys Are Accepted Through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/createNewEthereumIdentity.js:25-32`; documented in `SKILL.md:37-47` **Vulnerability Type**: Secret exposure through process arguments and shell history **Risk Level**: Medium ### Vulnerable Code `scripts/createNewEthereumIdentity.js:25-32`: ```js // Use provided key or generate a new one let privateKeyHex = args.key; if (!privateKeyHex) { privateKeyHex = new SigningKey(Wallet.createRandom().privateKey) .privateKey; } // Create signer from private key const signer = new SigningKey(addHexPrefix(privateKeyHex)); ``` The documented invocation explicitly encourages this input channel: ```bash node scripts/createNewEthereumIdentity.js --key 0x1234567890abcdef... node scripts/createNewEthereumIdentity.js --key 1234567890abcdef... ``` ### Technical Analysis The generic argument parser reads the private key from `process.argv` after the `--key` option. Command-line arguments are commonly observable through shell history, terminal session recording, automation logs, process inspection interfaces, endpoint monitoring, and crash or support diagnostics. Using a private key is part of the declared identity-import functionality, but passing it in the process argument vector is not the minimum safe method. The key can be exposed before the application has an opportunity to protect or erase it. ### Attack Path 1. A user follows the documented command and invokes the script with `--key <private-key>`. 2. The shell records the complete invocation in command history, or a process-monitoring tool captures the process argument vector while the script is running. 3. A local attacker, support operator, monitoring-system user, or later compromise gains access to that history or telemetry. 4. The attacker retrieves the private key from the recorded command. 5. The attacker imports the key into another wallet and impersonates the associated identity. ### Impact Assessment Exposure results in complete compro ...[truncated 338 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--key` command-line option for secret material. 2. Accept imported keys through a non-echoing interactive prompt, protected file descriptor, operating-system keychain, hardware wallet, or encrypted keystore. 3. If file-based import is required, require a file with mode `0600`, reject symbolic links, validate ownership, and delete or securely retain the source according to an explicit policy. 4. Update `SKILL.md` and `README.md` so they no longer instruct users to place private keys in command lines. 5. Warn users to remove any existing shell-history entries and review process-monitoring or automation logs that may already contain imported keys. 6. Minimize the lifetime of key strings in memory and never include them in errors or diagnostic output. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verifySignature.js:17
Finding
Verification Challenges Can Be Replayed Because They Do Not Expire or Get Consumed<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/challenge.js:8-20`; `scripts/verifySignature.js:17-58` **Vulnerability Type**: Authentication replay due to missing challenge expiration and one-time consumption **Risk Level**: Medium ### Vulnerable Code `scripts/shared/storage/challenge.js:8-20`: ```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); } ``` `scripts/verifySignature.js:17-58`: ```js const { kms, challengeStorage } = await getInitializedRuntime(); // 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); } ...[truncated 1979 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce a short challenge expiration period by loading and validating `created_at` during verification. 2. Atomically mark the challenge as consumed or delete it immediately after successful verification. 3. Ensure verification and consumption occur as a single atomic operation so concurrent replay attempts cannot both succeed. 4. Bind each challenge to the intended verifier, operation, recipient, and session identifier. 5. Store and reject previously processed JWS message identifiers, such as `id` or `thid`, for the duration of the replay-protection window. 6. Generate a fresh high-entropy challenge for every authentication attempt and invalidate any prior outstanding challenge according to a documented policy. 7. Add tests covering expired challenges, reused tokens, concurrent verification attempts, and challenges submitted in the wrong session. ]]>
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 (42)

Known Vulnerable Dependency: shell-quote==1.8.3 — 2 advisory(ies): CVE-2026-13311 (shell-quote: Quadratic-complexity Denial of Service in `parse()` (CWE-407)); CVE-2026-9277 (shell-quote quote() does not escape newlines in object .op values)

Critical
Category
Supply Chain
Confidence
97% confidence
Finding
shell-quote 1.8.3 is directly declared by the skill and is flagged for newline escaping and parse-complexity issues. In an agent skill that may build shell commands or parse shell-like input, this is especially dangerous because command-construction flaws can cross directly into command injection or reliable denial of service.

Known Vulnerable Dependency: shell-quote==1.8.3 — 2 advisory(ies): CVE-2026-13311 (shell-quote: Quadratic-complexity Denial of Service in `parse()` (CWE-407)); CVE-2026-9277 (shell-quote quote() does not escape newlines in object .op values)

Critical
Category
Supply Chain
Confidence
97% confidence
Finding
shell-quote 1.8.3 is flagged with advisories including a quadratic-complexity DoS in parse() and improper newline escaping in quote() for object op values. In an agent skill context, libraries that parse or construct shell fragments are especially risky because attacker-controlled input could cause denial of service or unsafe command construction if the package is used with untrusted data.

Credential Access

High
Category
Privilege Escalation
Content
| `identities.json`  | Identity metadata                               |
| `defaultDid.json`  | Active DID and associated public key            |
| `challenges.json`  | Per-DID challenge history                       |
| `credentials.json` | Verifiable credentials                          |

### Subprocess Execution Safety
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared purpose frames the skill as identity utilities, but the documented behavior also includes direct messaging through openclaw and child-process execution. That mismatch can mislead operators into approving a skill that can transmit signed artifacts or other data externally, increasing the risk of unintended exfiltration or misuse.

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
---
name: verified-agent-identity
description: Billions/Iden3 authentication and identity management tools for agents. Link, proof, sign, and verify.
metadata: { "category": "identity", "clawbot": { "requires": { "bins": ["node", "openclaw"] } }}
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 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.

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

```bash
cd scripts && npm install && cd
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
97% confidence
Finding
The skill explicitly documents that kms.json stores unencrypted private keys alongside other sensitive identity artifacts in a predictable path under $HOME/.openclaw/billions. If the agent host, another tool, or a lower-privileged process can read that directory, attackers can steal private keys, impersonate the agent, sign challenges, and access related credentials.

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 present and is reported as affected by memory disclosure and memory-exhaustion DoS issues. In an agent identity/authentication skill, WebSocket-based network interaction can be exposed to untrusted peers, so a remotely triggerable DoS or disclosure bug in a websocket stack is materially relevant.

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 is present and associated with multiple denial-of-service conditions due to pathological expansion behavior. Even as a transitive dependency, regex/glob-related DoS bugs matter if any attacker-controlled patterns reach matching logic during agent operations or tooling.

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 present with multiple host-confusion/SSRF-style advisories. In an authentication/identity skill that may resolve remote resources, DIDs, schemas, or verification endpoints, URL parsing ambiguities are especially dangerous because they can subvert trust boundaries and network allowlists.

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 present and vulnerable to memory exhaustion DoS. Because agent systems often maintain persistent network sessions, a websocket parsing/resource exhaustion bug can let an attacker degrade or crash the service remotely.

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 flagged for arbitrary code injection via unsafe evaluation, which is a serious class of issue. Even though it is transitive and may only be used in tooling paths, any evaluation of attacker-influenced JSONPath expressions could lead to code execution or severe compromise.

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 is associated with ReDoS/backtracking issues on crafted patterns. While often seen in build/tooling contexts, if any runtime path accepts user-controlled patterns, an attacker can trigger excessive CPU use 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
89% confidence
Finding
underscore 1.13.6 is present with unlimited recursion DoS in flatten/isEqual. If untrusted structured data can reach those helper paths, an attacker may crash the process with deeply nested objects/arrays.

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
95% confidence
Finding
undici 5.29.0 is present with multiple high-severity HTTP parsing, smuggling, poisoning, and injection advisories. In a network-facing identity skill that likely makes outbound HTTP requests to issuers, resolvers, or verifiers, this significantly increases exposure to SSRF, cache/protocol confusion, and request integrity issues.

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 present and affected by disclosure and memory-exhaustion issues. Multiple vulnerable websocket versions in the same lockfile broaden the attack surface and make exploitation more plausible in long-lived networked agent services.

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
78% confidence
Finding
Credential and identity data are persisted to predictable local JSON files, which can expose sensitive identity artifacts if the host filesystem is shared, backed up insecurely, or readable by other local users/processes. In an agent skill handling authentication material, local plaintext persistence increases the chance of credential disclosure and unauthorized reuse.

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding
The README instructs users to run `npx clawhub@latest install ...`, which fetches and executes the latest remote package version at install time rather than a pinned, reviewed version. That creates a supply-chain risk: if the package or one of its release channels is compromised, users may execute attacker-controlled code during installation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding
This installation command again relies on `npx clawhub@latest`, which means the executed code can change over time without review by the skill user. In a security-sensitive identity-management skill, running an unpinned installer is especially risky because compromise at install time could expose private keys, credentials, or messaging workflows.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly states that private keys are persisted unencrypted in `$HOME/.openclaw/billions/kms.json`, but it does not provide a clear warning about the sensitivity of that file or the consequences of compromise. For an identity toolkit, loss of these keys can enable account takeover, impersonation, fraudulent proof generation, and long-term compromise of linked identities.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill requires Node.js execution, external binaries, filesystem access under $HOME, and network/message-sending behavior, but it does not declare any explicit tool scope or allowed-tools boundaries. In an agent environment this weakens least-privilege controls and can cause the skill to be invoked with broader capabilities than users or orchestrators expect.

Vague Triggers

Medium
Confidence
85% confidence
Finding
The invocation criteria are broad enough to match common identity or authentication requests, which may cause the agent to select this skill in situations where signing, linking, or token handling was not intended. Because the skill can create identities and send signed responses, over-triggering increases the chance of sensitive actions occurring without sufficiently specific user consent.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The documentation encourages passing an existing private key on the command line without a prominent warning, even though command-line arguments may be exposed via shell history, process listings, logs, or agent telemetry. This creates a realistic path for accidental secret disclosure of long-lived identity keys.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code imports a private key into KMS storage and then persists the resulting DID metadata, but the only visible user-facing output is the final success message. There is no confirmation prompt, explicit disclosure, or warning that sensitive key material will be stored and a default identity record will be written.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The code creates persistent file storage for KMS material using "kms.json", which is a safety-relevant write of cryptographic key data. Although there are developer comments describing the function, there is no user-facing warning, confirmation, or visible disclosure that sensitive keys and related identity data will be written to local files.

Missing User Warnings

Medium
Confidence
77% confidence
Finding
The runtime is configured to contact external services at the Billions Network RPC endpoint and the PolygonID RHS endpoint, which may transmit user or system-derived identity and credential state over the network. The file contains no user-facing warning, confirmation, or visible notice that external network calls will occur during runtime initialization and credential status handling.

Static analysis

No suspicious patterns detected.