Back to skill

Security audit

Verified Agent Identity

Security checks for vulnerabilities and agentic risk

Overview

This identity skill appears purpose-aligned, but it needs review because it manages agent signing keys and can store private keys in plaintext unless encryption is configured.

Install only if you are comfortable letting the skill create, import, persist, and use agent identity signing keys. Set BILLIONS_NETWORK_MASTER_KMS_KEY before creating or importing identities, avoid passing real private keys on the command line, restrict permissions on $HOME/.openclaw/billions, prefer a pinned installer version, and do not rely on signed challenges as fresh authentication unless replay protection is added.

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
.tmp` temporary path is also created without exclusive-file or symbolic-link protections. This increases exposure during writes and may allow interference when an attacker already has write access to the storage directory. ### Attack Path 1. A user creates or imports an identity without configuring `BILLIONS_NETWORK_MASTER_KMS_KEY`. 2. The Skill serializes the identity's raw private key using the `plain` provider. 3. `FileStorage.writeFile` writes that data to the predictable `kms.json.tmp` path and renam ...[truncated 1157 chars]:48
Finding
Private Keys Are Stored in Plaintext Without Enforced Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:48-61`; `scripts/shared/storage/base.js:27-32` **Vulnerability Type**: Plaintext sensitive-data storage and unsafe file permissions **Risk Level**: High ### Vulnerable Code `scripts/shared/storage/keys.js:48-61`: ```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 }, }; } ``` `scripts/shared/storage/base.js:27-32`: ```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 When `BILLIONS_NETWORK_MASTER_KMS_KEY` is absent or rejected, `_encodeEntry` deliberately serializes the raw private key into `kms.json` with the provider set to `plain`. Encryption is therefore optional even though the stored material controls the agent's decentralized identity. The generic file-storage implementation creates the directory and temporary file without explicitly setting restrictive modes. Access is consequently determined by the process umask and pre-existing directory permissions. In an environment with permissive permissions, another local account or process may be able to read the key file. The predictable `<file>.tmp` temporary path is also created without exclusive-file or symbolic-link protections. This increases exposure during writes and may allow interference when an attacker already has write access to the storage directory. ### Attack Path 1. A user creates or imports an identity without configuring `BILLIONS_NETWORK ...[truncated 1220 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require encrypted private-key storage and fail closed when no valid master key is configured. Do not silently fall back to plaintext. 2. If password-like master secrets are supported, derive the encryption key with a password KDF such as Argon2id or scrypt using a unique random salt and documented parameters. 3. Create `$HOME/.openclaw/billions` with mode `0700` and key files with mode `0600`, independent of the process umask. 4. Validate that the storage directory and destination are owned by the expected user and are not symbolic links. 5. Use a cryptographically random temporary filename in the same directory, create it with exclusive semantics, set mode `0600`, flush it, and then atomically rename it. 6. Check and correct the permissions of legacy `kms.json` files during migration. 7. Provide a secure migration path that encrypts existing plaintext entries after a master key is configured. 8. Avoid accepting private keys directly on the command line where feasible because command-line arguments may be exposed through shell history or process inspection. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verifySignature.js:18
Finding
Authentication Challenges Can Be Replayed Indefinitely<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/challenge.js:8-20`; `scripts/verifySignature.js:18-56` **Vulnerability Type**: Replayable authentication challenge **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:18-56`: ```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( ` ...[truncated 2240 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Assign each challenge a short expiration time and reject records older than the configured lifetime. 2. Atomically consume or delete a challenge immediately after successful verification so concurrent requests cannot verify it more than once. 3. Store an explicit challenge state such as `issued`, `consumed`, and `expired`, together with issuance and consumption timestamps. 4. Bind the signed challenge to the intended verifier, audience, operation, and session identifier to prevent reuse in another context. 5. Use a unique high-entropy nonce for each verification attempt and retain consumed nonce identifiers for at least the maximum token lifetime. 6. Ensure the consume operation is atomic across concurrent processes, using a transactional store or an appropriate filesystem lock. 7. Avoid placing JWS tokens in shell history or persistent logs, and redact them from error reporting and telemetry. ]]>
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 (31)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The advertised purpose is identity linking and proof verification, but the skill also manages private keys, reads and writes a KMS store, and supports key import. That broader behavior materially changes the trust boundary: a user enabling a verification skill may unknowingly grant a key-management skill access to highly sensitive secrets and persistent local state.

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
84% confidence
Finding
The skill stores verifiable credentials and related identity artifacts locally, which constitutes sensitive credential access and persistence. While this is expected for identity software, it is still security-relevant because compromise of the local storage directory could expose credentials, metadata, or linkage information useful for impersonation or privacy attacks.

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
ws 8.18.0 is flagged for both uninitialized memory disclosure and memory exhaustion via fragmented frames. In an agent identity skill that may maintain websocket connections to blockchain or RPC services, these flaws can become materially relevant because hostile peers or intermediaries could trigger information leakage or denial of service.

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
93% confidence
Finding
brace-expansion 2.0.2 has multiple denial-of-service issues caused by pathological expansion input. While often used in tooling or build-related paths, the vulnerable version remains part of the shipped dependency graph and could be abused wherever untrusted patterns are processed, causing CPU 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
97% confidence
Finding
fast-uri 3.1.0 is reported vulnerable to multiple URI parsing issues, including host confusion and SSRF-relevant normalization bugs. This is especially concerning in an identity skill that may fetch schemas, DID documents, attestations, or registry data from remote endpoints, because malformed attacker-controlled URLs could bypass origin or host checks.

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
97% confidence
Finding
ws 7.5.10 is vulnerable to memory exhaustion DoS via tiny fragments and data chunks. Because this skill’s ecosystem includes RPC and websocket-capable packages, a malicious endpoint could potentially force resource exhaustion and destabilize the agent process.

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
94% confidence
Finding
underscore 1.13.6 is flagged for unlimited recursion in flatten and isEqual, enabling denial of service on crafted nested input. If attacker-controlled structured data reaches code paths using these helpers, the agent can crash or hang due to stack exhaustion or excessive processing.

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
98% confidence
Finding
undici 5.29.0 is reported vulnerable to multiple serious HTTP issues, including request/response smuggling, queue poisoning, and CRLF-related problems. In a skill that likely communicates with remote identity, registry, or blockchain-adjacent services, HTTP client flaws substantially raise risk because remote interactions are central to normal operation.

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
98% confidence
Finding
ws 8.17.1 is flagged for uninitialized memory disclosure and memory exhaustion DoS. Since websocket support is common in blockchain/RPC stacks, this vulnerable version is a meaningful risk in the context of an agent identity skill that may connect to untrusted or semi-trusted network 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
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.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The README instructs users to execute `npx clawhub@latest install ...`, which fetches and runs the latest package version at execution time rather than a fixed, reviewed version. This creates a supply-chain risk: if the package is compromised or a breaking/malicious release is published, users may execute untrusted code during installation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The human installation instructions also use `npx clawhub@latest`, causing arbitrary code execution risk through an unpinned package install path. Because this is a setup command intended for end users, the attack surface is broader: any compromise of the upstream package or publishing account could affect all installers.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill invokes Node scripts that handle identities, local key material, and remote registry interactions, yet it declares no explicit tool scope or permissions boundary. In an agent setting, missing scope declarations increases the chance the agent can access environment variables and network resources without clear user visibility or policy enforcement.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill instructs users to pass raw private keys on the command line, which is unsafe because command-line arguments are often exposed in shell history, process listings, logs, and agent traces. Without a prominent warning or safer input mechanism, sensitive key material can be leaked accidentally during normal use.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation admits private keys may be stored in plaintext if the master KMS key is not set, but this appears only later in the document rather than as a blocking setup warning. In this identity-management context, plaintext storage of private keys creates a direct compromise path for agent identity takeover and credential abuse.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code initializes key storage with a file-backed `KeysFileStorage("kms.json")`, which means private key material is persisted to local disk. In an agent identity/authentication skill, storing long-lived signing keys unencrypted or without explicit operator consent materially increases the risk of key theft, agent impersonation, and unauthorized signing if the host filesystem is accessible or backed up insecurely.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
Credential and identity data are written to `credentials.json`, `identities.json`, and `profiles.json` on local storage. In a decentralized identity skill, these files can contain sensitive personal and authentication metadata, so local plaintext persistence can leak identity relationships, credentials, or profile data to other local users, malware, or misconfigured backups.

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
95% confidence
Finding
The lockfile includes uuid 13.0.0, which is reported vulnerable to a missing buffer bounds check when v3/v5/v6 APIs are called with a caller-supplied buffer. Even though this file is only a dependency manifest, the presence of the vulnerable version means the packaged skill can expose consumers to crashes or undefined behavior if those code paths are reachable.

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
95% confidence
Finding
uuid 9.0.1 is also covered by the same bounds-check issue affecting certain namespace-based UUID generation APIs when a buffer argument is provided. This is a real supply-chain exposure because the vulnerable package is present in the resolved dependency set, even if exploitability depends on runtime usage.

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
88% confidence
Finding
ajv 8.17.1 is reported vulnerable to ReDoS when the $data option is enabled and attacker-controlled schemas or values are validated. In this identity-oriented skill, JSON schema processing is plausible, so the dependency should be treated as a genuine risk even though exploitability depends on specific application configuration.

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

Low
Category
Supply Chain
Confidence
84% confidence
Finding
elliptic 6.6.1 is flagged for use of a risky cryptographic implementation. In a decentralized identity and proof-related skill, cryptographic correctness matters more than in ordinary apps, so retaining a flagged crypto primitive increases assurance risk even if no immediate remote exploit is guaranteed from the lockfile alone.

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

Low
Category
Supply Chain
Confidence
95% confidence
Finding
uuid 8.3.2 is another affected version for the same bounds-check flaw in certain generation functions. Its presence in the dependency tree constitutes a real vulnerability exposure, though the practical impact depends on whether those specific APIs are invoked with attacker-influenced buffer parameters.

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

Low
Category
Supply Chain
Confidence
86% confidence
Finding
stream-json 1.9.1 is reported to have O(depth²) behavior on deeply nested input for certain filters, which can enable algorithmic complexity attacks. This is likely only exploitable if the package processes attacker-controlled JSON in affected modes, but the dependency still represents a genuine low-severity DoS risk.

Static analysis

No suspicious patterns detected.