Back to skill

Security audit

Agent Desapetc 999

Security checks for vulnerabilities and agentic risk

Overview

The skill does identity work as advertised, but it creates long-lived signing keys that are stored in plaintext by default and uses weak, reusable authentication challenges.

Install only if you are comfortable with this skill creating and storing agent identity keys on this machine. Set BILLIONS_NETWORK_MASTER_KMS_KEY before creating any identity, restrict access to $HOME/.openclaw/billions, avoid passing wallet private keys on the command line, and treat generated signed tokens as reusable secrets until the challenge handling is fixed.

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:50
Finding
Private Keys Are Stored in Plaintext by Default Without Enforced Owner-Only Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:50-56`, `scripts/shared/storage/crypto.js:11-25`, `scripts/shared/storage/base.js:9-12,27-32` **Vulnerability Type**: Plaintext sensitive-data storage and insufficient filesystem permission enforcement **Risk Level**: High ### Vulnerable Code `scripts/shared/storage/keys.js:50-56`: ```js return { version: 1, provider: "plain", data: { alias, key: privateKeyHex, createdAt }, }; ``` The corresponding plaintext decoding behavior is: ```js if (entry.provider === "plain") { return { alias, privateKeyHex: key, createdAt }; } ``` `scripts/shared/storage/crypto.js:11-25`: ```js function getMasterKey() { const rawKey = process.env.BILLIONS_NETWORK_MASTER_KMS_KEY; if (typeof rawKey !== "string") { return null; } const trimmedKey = rawKey.trim(); const MIN_MASTER_KEY_LENGTH = 16; if (trimmedKey.length < MIN_MASTER_KEY_LENGTH) { return null; } return trimmedKey; } ``` `scripts/shared/storage/base.js:9-12,27-32`: ```js async ensureDirectory() { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); } 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 Skill stores identity private keys under `$HOME/.openclaw/billions/kms.json`. Encryption is optional: when `BILLIONS_NETWORK_MASTER_KMS_KEY` is missing or shorter than 16 characters, `getMasterKey()` returns `null`, and `KeysFileStorage` serializes the raw private key as a plaintext hexadecimal string. An invalid but configured master key is treated identically to an absent key. The operation does not fail or warn that sensitive key material will be stored without encryption. The storage layer also does not explicitly enforce mode `0700` on the cont ...[truncated 1967 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Fail closed for private-key creation** - Require a valid master key before creating or importing long-lived private keys. - If plaintext storage must remain available for compatibility, require an explicit insecure-development flag and display a prominent warning. - Treat a configured but invalid master key as an error rather than silently falling back to plaintext. 2. **Enforce restrictive filesystem permissions** - Create `$HOME/.openclaw/billions` with mode `0700`. - Create key files and temporary files with mode `0600`. - Validate and correct permissions on existing files before reading or updating them. - Refuse to use paths owned by another account or paths that resolve through unsafe symbolic links. 3. **Harden temporary-file handling** - Use a randomized, exclusively created temporary file in the same directory. - Open it with an exclusive-creation flag and mode `0600`. - Flush file contents before atomic rename where durability is required. - Clean up temporary files on failure. 4. **Improve master-key handling** - Clearly report whether encrypted storage is active. - Use a proper password-based KDF such as Argon2id or scrypt with a random salt if human-memorable passphrases are accepted. - Prefer a platform secret store or hardware-backed key manager where available. 5. **Migrate existing installations** - Detect plaintext entries at startup. - Provide a safe migration operation that encrypts all plaintext keys after a master key is configured. - Warn users that previously exposed keys may require rotation, since encryption after exposure cannot restore confidentiality. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generateChallenge.js:17
Finding
Authentication Challenges Do Not Expire or Become Invalid After Successful Verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generateChallenge.js:17-21`, `scripts/shared/storage/challenge.js:9-24`, `scripts/verifySignature.js:19-22,47-54` **Vulnerability Type**: Replayable authentication proof and insufficient challenge entropy **Risk Level**: High ### Vulnerable Code `scripts/generateChallenge.js:17-21`: ```js // Generate random challenge const challenge = randomInt(0, 10000000000).toString(); // Save challenge to storage await challengeStorage.save(args.did, challenge); ``` `scripts/shared/storage/challenge.js:9-24`: ```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:19-22`: ```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); } ``` `scripts/verifySignature.js:47-54`: ```js // 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); } ``` ### Technical Analysis Challenge generation selects a decimal value between zero and 9,999,999,999. This provides approximately 33 bits of entropy, substantially below the conventional minimum of 128 random bits for security-sensitive nonces. The storage layer records `created_at`, but verification does not retrieve or inspect that timestamp. As a result, challenges have no enforced lifetime. More importantly, ...[truncated 2220 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Use high-entropy challenges** - Generate at least 128 bits with `crypto.randomBytes(16)` or more. - Encode the challenge using base64url or hexadecimal notation. - Do not use a bounded decimal integer as an authentication nonce. 2. **Enforce expiration** - Store an explicit expiration time with each challenge. - Reject challenges older than a short, documented interval, such as five minutes. - Delete expired entries during generation and verification. 3. **Make challenges single-use** - Atomically consume or invalidate the challenge during successful verification. - Ensure two concurrent verification attempts cannot both accept the same challenge. - Store a consumed state if audit history is required, without retaining an active reusable challenge. 4. **Bind challenges to context** - Include the intended verifier, operation, session identifier, DID, and purpose in the signed payload. - Verify every contextual field rather than checking only `from` and `body.message`. - Prevent a proof created for one workflow from being accepted in another workflow. 5. **Minimize token exposure** - Avoid placing authentication tokens in logs or URLs. - Redact tokens from errors and diagnostic output. - Document that signed authentication tokens remain sensitive even though they do not contain private keys. 6. **Add security tests** - Test that an expired challenge is rejected. - Test that a successfully used token cannot be replayed. - Test concurrent verification attempts against the same challenge. - Test that challenges meet the required entropy and format constraints. ]]>
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 (37)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill is presented as an identity-verification utility, but it also manages and persists private key material locally, including storing keys in plaintext when BILLIONS_NETWORK_MASTER_KMS_KEY is not set. That hidden operational behavior materially changes the trust model: using the skill can create long-lived credential assets on disk that may later be stolen or misused.

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).

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
97% confidence
Finding
The skill explicitly documents storage of sensitive identity artifacts, including private keys in kms.json and credentials in credentials.json, under a predictable location in the user's home directory. In an agent ecosystem, discoverable local credential stores are high-value targets for exfiltration, and the risk is amplified because the private keys may be plaintext if no master key is configured.

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
97% confidence
Finding
ws 8.18.0 is flagged for memory disclosure and memory-exhaustion denial of service issues, both of which are meaningful in software that may maintain WebSocket connections to wallets, RPC endpoints, relays, or browsers. In an agent identity skill that may communicate with external services, a vulnerable WebSocket stack increases exposure to remote crashes or information leakage.

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 algorithmic complexity and expansion-related DoS advisories, making it a legitimate vulnerable dependency instance. Even as a transitive package, it can be dangerous if any path allows attacker-controlled glob or pattern expansion, potentially causing hangs or memory exhaustion.

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 flagged for multiple URI parsing/canonicalization issues including SSRF and host confusion classes, which are highly relevant in software that may fetch schemas, DID documents, JSON-LD contexts, or remote attestations. In a decentralized identity skill, malformed URL handling can directly affect trust decisions or outbound network access.

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
96% confidence
Finding
ws 7.5.10 is affected by a memory exhaustion DoS issue and is an older major line than the other ws instances, making it a real and relevant weakness. If reachable through JSON-RPC/WebSocket functionality, a remote peer could trigger excessive resource consumption and disrupt agent operations.

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
91% confidence
Finding
underscore 1.13.6 is flagged for unlimited recursion in functions like _.flatten and _.isEqual, creating a denial-of-service risk on crafted nested input. Since this arrives through tooling-style dependencies, exploitability may be indirect, but if exposed in request processing it can crash or stall the process.

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
97% confidence
Finding
undici 5.29.0 has numerous reported HTTP parsing and request-handling issues, including smuggling, queue poisoning, and injection classes, making this a significant network-facing dependency risk. This is particularly concerning in an identity skill that may retrieve remote DID documents, schemas, attestations, or registry data over HTTP.

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
97% confidence
Finding
ws 8.17.1 is a real vulnerable WebSocket dependency affected by memory disclosure and memory exhaustion issues. Because agent and blockchain ecosystems commonly use persistent socket-based RPC or event streams, this can become remotely reachable and materially affect confidentiality and availability.

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
When no master key is available, _encodeEntry falls back to provider "plain" and writes privateKeyHex directly to kms.json. Storing agent private keys unencrypted on disk creates immediate compromise risk from local file disclosure, backups, logs, container escapes, or accidental source/control artifact inclusion; in an identity/authentication skill, stolen keys can let an attacker impersonate an agent or generate fraudulent proofs.

Missing User Warnings

High
Confidence
96% confidence
Finding
The list() method returns raw private key material for every stored entry rather than just metadata such as aliases. Any caller with access to this API can bulk-exfiltrate all keys in one operation, greatly increasing blast radius and making accidental disclosure through debugging, logging, or downstream serialization much more likely; in this identity-focused skill, that enables agent impersonation and unauthorized proof generation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The README instructs users to execute `npx clawhub@latest install ...`, which pulls and runs the latest package version at install time. Using a floating installer version weakens supply-chain integrity because a compromised or malicious future release could be executed automatically by users or agents following the documentation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
This installation command again relies on `npx clawhub@latest`, causing execution of whatever package version is current at the time the command is run. In an agent-installation context this is especially risky because users may copy-paste the command without independently validating the package version or contents.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The documentation tells users to pass an Ethereum private key directly on the command line via `--key <your-ethereum-private-key>`. Command-line secrets are commonly exposed through shell history, process listings, terminal logs, and agent telemetry, which can lead to irreversible compromise of the wallet and any identity derived from it.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill invokes Node scripts that use environment configuration and external network interactions, but it does not declare any explicit tool scope or allowed-tools boundary. In an agent setting, missing capability declarations weakens least-privilege enforcement and can allow the skill to be triggered with broader execution authority than users or orchestrators expect.

Vague Triggers

Medium
Confidence
87% confidence
Finding
The activation guidance is broad and maps to very common identity-related requests, which increases the chance that an agent auto-invokes the skill in situations where the user did not intend key creation, signing, or identity linking. Because the skill performs sensitive authentication operations, over-broad trigger conditions raise the risk of accidental credential use and privacy-impacting actions.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The KMS is backed by `KeysFileStorage("kms.json")`, which persists private key material to a local file. For an agent identity/authentication skill, local plaintext or weakly protected key storage materially increases the risk of key theft, agent impersonation, and unauthorized signing if the host filesystem is exposed.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
Credential and identity records are written to local files (`credentials.json`, `identities.json`, `profiles.json`) without any visible protection or disclosure in this file. In an identity wallet context, these files may contain sensitive attestations, identifiers, and profile metadata that could be harvested for privacy loss, correlation, or follow-on attacks if the system is compromised.

Vague Triggers

Low
Confidence
82% confidence
Finding
The example phrase 'Link your agent identity to me' is generic enough to cause opportunistic activation from ordinary conversation. In this context, that can push an agent toward executing a sensitive identity-linking workflow without sufficient validation of the requester, purpose, or consent.

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
92% confidence
Finding
The lockfile pins uuid 13.0.0 in a transitive dependency, and the cited advisory affects v3/v5/v6 when a caller supplies an output buffer that is too small. This is a real supply-chain risk, though typically low impact because exploitation depends on specific application usage patterns rather than mere installation.

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
90% confidence
Finding
uuid 9.0.1 is also affected by the same missing bounds check issue, making this a duplicate but still real vulnerable package instance. The practical risk remains low unless code paths use name-based UUID generation with attacker-controlled buffer handling.

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
83% confidence
Finding
ajv 8.17.1 is reported vulnerable to ReDoS when the $data option is enabled, so this is a real issue only if schemas are evaluated in that mode with attacker-controlled inputs. Given this skill processes identity- and proof-related structured data, schema validation may be reachable, but the exploitability depends on runtime configuration not visible in the lockfile alone.

Static analysis

No suspicious patterns detected.