Back to skill

Security audit

Abdullahi AI Agent

Security checks for vulnerabilities and agentic risk

Overview

The skill is broadly an identity-linking tool, but it handles long-lived identity private keys and verification flows with enough unsafe defaults to require review before installation.

Review before installing. Set BILLIONS_NETWORK_MASTER_KMS_KEY before creating or importing identities, avoid passing real private keys on the command line, restrict access to $HOME/.openclaw/billions, and treat this skill's verification as sensitive because signed challenges and stored keys can prove control of an identity. Also review the network endpoints and update or audit dependencies before using it in a high-trust environment.

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:47
Finding
Private Keys Stored in Plaintext Without Enforced Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:47-60`; `scripts/shared/storage/base.js:9-12,26-31` **Vulnerability Type**: Plaintext storage of cryptographic keys and insufficient filesystem permission enforcement **Risk Level**: High ### Vulnerable Code `scripts/shared/storage/keys.js:47-60`: ```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:9-12,26-31`: ```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 key-storage implementation makes encryption optional. If `BILLIONS_NETWORK_MASTER_KMS_KEY` is absent, whitespace-only, or shorter than the accepted minimum, `_encodeEntry` silently writes the raw private key to `kms.json` using the `plain` provider. The generic storage implementation creates the containing directory and temporary file without specifying secure modes. Consequently, access permissions depend on the process umask. Common umask settings can result in a directory readable or traversable by other local users and a file created with permissions such as `0644`. The temporary file `${this.filePath}.tmp` also contains the complete serialized key material while a write is in progress. It is created without `O_EXCL`, a restrictive mode, or validation that it is a regular file ...[truncated 1116 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make encrypted key storage mandatory. Fail closed if no valid master key or protected operating-system key store is available. 2. Create `$HOME/.openclaw/billions` with mode `0700`. 3. Create `kms.json` and temporary files with mode `0600`, and explicitly enforce these permissions after replacement. 4. Use a uniquely named temporary file in the same directory, opened with exclusive creation semantics such as `O_CREAT | O_EXCL`. 5. Reject symbolic links and verify that both the destination and temporary path are regular files owned by the current user. 6. Flush the temporary file before atomic replacement where durability is required. 7. Prefer an operating-system credential vault, hardware-backed key store, or dedicated encrypted KMS instead of application-managed plaintext files. 8. Detect legacy plaintext entries and require an explicit, secure migration to encrypted storage. 9. Document key rotation procedures and advise users to rotate keys that may already have been stored with permissive access. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/verifySignature.js:17
Finding
Authentication Challenges Can Be Replayed Indefinitely<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generateChallenge.js:17-23`; `scripts/verifySignature.js:17-22,46-58`; `scripts/shared/storage/challenge.js:8-22,30-32` **Vulnerability Type**: Missing challenge expiration and single-use enforcement **Risk Level**: High ### Vulnerable Code `scripts/generateChallenge.js:17-23`: ```js // Generate random challenge const challenge = randomInt(0, 10000000000).toString(); // Save challenge to storage await challengeStorage.save(args.did, challenge); outputSuccess(challenge); ``` `scripts/verifySignature.js:17-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:46-58`: ```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); } outputSuccess("Signature verified successfully"); ``` `scripts/shared/storage/challenge.js:8-22,30-32`: ```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); } async getChallenge(did) { const entry = await this.find(did); return entry?.challenge; } ``` ### Technical Analysis The verifier checks that the signed message equals the challenge currently stored for a DID, but it do ...[truncated 1708 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate challenges with at least 128 bits of cryptographically secure randomness, for example `randomBytes(32).toString("base64url")`. 2. Store an explicit expiration timestamp, intended verifier, session identifier, and purpose with each challenge. 3. Reject expired challenges using a short, documented validity period. 4. Atomically consume or delete the challenge when verification succeeds. 5. Ensure concurrent verification attempts cannot both consume the same challenge by using locking, transactional storage, or an atomic compare-and-delete operation. 6. Bind the signed payload to the verifier, expected audience, DID, session, and operation to prevent cross-context replay. 7. Avoid including challenge contents in detailed error messages where those messages may be exposed to untrusted callers. 8. Add tests covering successful one-time use, replay rejection, expiration, concurrent submissions, and challenge replacement. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/createNewEthereumIdentity.js:24
Finding
Private Key Import Through Command-Line Arguments Exposes Secrets<![CDATA[ ## Vulnerability Details **File Location**: `scripts/createNewEthereumIdentity.js:24-31`; `scripts/shared/utils.js:75-85`; documented in `SKILL.md:37-48` **Vulnerability Type**: Sensitive information exposed through process arguments and command history **Risk Level**: Medium ### Vulnerable Code `scripts/createNewEthereumIdentity.js:24-31`: ```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)); ``` `scripts/shared/utils.js:75-85`: ```js function parseArgs() { const args = {}; for (let i = 2; i < process.argv.length; i++) { if (process.argv[i].startsWith("--")) { const key = process.argv[i].slice(2); const value = process.argv[i + 1]; args[key] = value; i++; } } return args; } ``` `SKILL.md:37-48`: ```bash node scripts/createNewEthereumIdentity.js [--key <privateKeyHex>] # Generate a new random identity node scripts/createNewEthereumIdentity.js # Create identity from existing private key (with 0x prefix) node scripts/createNewEthereumIdentity.js --key 0x1234567890abcdef... # Create identity from existing private key (without 0x prefix) node scripts/createNewEthereumIdentity.js --key 1234567890abcdef... ``` ### Technical Analysis The documented import workflow places the complete private key in the process argument vector. Command-line arguments are not an appropriate secret transport mechanism because they may be retained in shell history, agent tool-call records, terminal transcripts, monitoring systems, audit logs, crash reports, and process listings. The implementation reads the secret directly from `process.argv`, so exposure occurs before any cryptographic processing or encrypted storage. Encrypting the eventual `kms.json` file does not mitigate ...[truncated 1048 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove or deprecate the `--key` command-line option. 2. Accept private keys through a hidden interactive prompt that disables terminal echo, protected standard input, or a dedicated file descriptor. 3. If file-based import is necessary, require a caller-owned file with mode `0600`, validate ownership and type, and avoid logging its contents. 4. Avoid environment variables for long-lived private keys because they can also be exposed through process environments and diagnostics. 5. Redact secret-bearing arguments from agent logs and telemetry during the deprecation period. 6. Update `SKILL.md` and `README.md` so examples never place real private keys in command lines. 7. Warn users who previously used `--key` to remove affected shell-history entries and rotate the imported keys where exposure may have occurred. ]]>
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 (36)

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                                                             |

There are several ways of storing private keys, to enable master key encryption as described in the **KMS Encryption** section below.
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
93% confidence
Finding
The documented behavior makes strong claims about Billions, ERC-8004, attestation registries, and iden3-based proof workflows, but the visible skill content mainly describes local scripts and file storage without evidence of those security-critical verification flows. A mismatch between stated security properties and actual behavior can cause operators to trust identity linking or proof generation that may not really occur, leading to authentication bypass or false assurance.

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
81% confidence
Finding
The skill documents storage of verifiable credentials and related identity artifacts in a predictable directory, which concentrates sensitive material in one location. Even though merely naming credentials.json is not exploit code by itself, in this context it increases the risk of credential harvesting by other tools, plugins, or local users if filesystem protections are weak.

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 fragmentation-based memory exhaustion DoS, both of which are meaningful for software that accepts WebSocket connections or processes hostile WebSocket peers. Given this identity-oriented agent stack includes networking and blockchain tooling, a vulnerable WebSocket implementation in the dependency tree increases risk if any exposed service or upstream connection uses it.

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
80% confidence
Finding
brace-expansion 2.0.2 has multiple denial-of-service issues involving pathological expansion patterns that can hang or exhaust memory. In this lockfile it appears as a transitive, likely build/tooling-oriented dependency, so the underlying issue is real but probably less dangerous unless runtime code accepts attacker-controlled glob or brace patterns.

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
92% confidence
Finding
fast-uri 3.1.0 is flagged for multiple host-confusion and SSRF-related parsing flaws, which are significant wherever untrusted URLs are validated or normalized before outbound requests. This matters more in this skill because identity/attestation flows often fetch remote schemas, DID documents, or verifier resources, making malformed URL handling more security-relevant than in a purely local package.

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
93% confidence
Finding
ws 7.5.10 is vulnerable to memory exhaustion DoS from fragmented frames/data chunks, which can let a hostile peer consume excessive resources. Since this package tree includes RPC/WebSocket infrastructure, the issue is meaningful if any service or client session can be influenced by an attacker-controlled endpoint.

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
88% confidence
Finding
underscore 1.13.6 has uncontrolled recursion risks in _.flatten and _.isEqual that can cause denial of service with crafted deeply nested data. This is a real issue, though likely transitive and only dangerous if exposed to attacker-controlled structured input at runtime.

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 flagged for numerous HTTP parsing/smuggling/poisoning issues, which are high-value vulnerabilities in any component making or proxying HTTP requests. This is especially relevant in an identity-verification skill that may contact remote registries, schema endpoints, DID resolvers, or attestation services, where malformed upstream traffic could be weaponized.

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 affected by both memory disclosure and memory exhaustion DoS issues, making it a substantive network-facing dependency risk. In this package set, multiple blockchain/RPC libraries rely on WebSockets, so the skill context makes this more dangerous than a purely offline tool because remote peers may influence connection behavior.

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
72% confidence
Finding
The module persists credential data to a predictable local file (`credentials.json`), which can expose sensitive identity artifacts if the host is multi-user, backups are accessible, or the file store lacks encryption and strict permissions. In an agent identity skill, stored credentials may enable profiling, correlation, or unauthorized reuse depending on what the credential wallet writes.

Missing User Warnings

High
Confidence
98% confidence
Finding
When no master key is present, _encodeEntry stores private keys in plaintext on disk under provider: "plain", with no hard failure or explicit safeguard. For a decentralized identity skill handling authentication proofs and identity-linked keys, plaintext local storage creates a straightforward compromise path via filesystem access, backups, logs, container escapes, or accidental inclusion in source/control artifacts.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The README instructs users to run `npx clawhub@latest install verified-agent-identity`, which fetches and executes the latest remote package version at install time rather than a fixed, reviewed version. This creates a supply-chain risk: if the package or its distribution channel is compromised, users may execute attacker-controlled code during installation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The human installation instructions also use `npx clawhub@latest`, causing arbitrary current package code to be executed from the registry. Because this skill manages identity material and can lead users to generate or link credentials, a compromised installer could have meaningful security consequences.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill declares executable behavior that relies on Node, environment configuration, local sensitive storage, and likely network interaction, but it does not declare any explicit tool scope or permissions. This creates an authorization gap where an agent may be induced to access env variables or make networked identity operations without clear sandboxing or least-privilege controls.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill explicitly states that private keys in kms.json may be stored in plaintext when BILLIONS_NETWORK_MASTER_KMS_KEY is unset, but it does not prominently warn users before identity creation or treat encryption as mandatory. This exposes long-lived identity secrets to local compromise, backup leakage, or accidental disclosure, which can allow full impersonation of the agent identity.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code sends the full authorization request payload to an external URL shortener service, which exposes identity-verification metadata and challenge contents to a third party outside the trust boundary. In an identity-linking skill, that data can reveal sensitive verification context, correlate users and agents, or allow logging and replay analysis by the shortener operator or anyone with access to its records.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The code initializes `KeysFileStorage("kms.json")`, which indicates private key material is persisted to disk. While there are code comments, they describe component setup rather than warning that sensitive cryptographic material and related identity artifacts will be written locally, so this file lacks a clear user disclosure for a safety-sensitive operation.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The credential and identity storage setup uses file-backed stores for `credentials.json`, `identities.json`, and `profiles.json`, which implies persistent writes of potentially sensitive user data. The file contains no user-facing log, warning comment, or other disclosure explaining that these records are stored on disk.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The file configures connections to `rpc-mainnet.billions.network` and `rhs-staging.polygonid.me`, which are external services and may receive wallet, credential-status, or related metadata during operation. Although comments describe configuration, there is no explicit warning that the skill performs outbound network requests to third-party endpoints.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The list() method returns every alias together with its raw privateKeyHex, which unnecessarily broadens access to secret key material beyond simple key discovery or metadata enumeration. In an identity/attestation skill, exposing all private keys through a bulk listing API increases the blast radius of any misuse, logging, or downstream bug and can directly enable impersonation of agent or human-linked identities.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This script signs an arbitrary caller-supplied challenge and emits a valid authentication token without any explicit user confirmation, policy check, or origin validation. In an identity/authentication skill, silent signing is dangerous because any process able to invoke the script can obtain proofs tied to the agent's DID, enabling unauthorized authentication, replay into trusting systems, or abuse of the agent's linked human identity context.

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
86% confidence
Finding
The lockfile pins uuid 13.0.0, and the cited issue affects v3/v5/v6 generation when a caller supplies a destination buffer that is too small. This is a real dependency risk, but its exploitability depends on application code invoking the affected APIs with attacker-influenced parameters; in a lockfile alone we cannot prove a reachable path.

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
84% confidence
Finding
uuid 9.0.1 carries the same missing bounds-check issue for specific UUID generation variants when a buf parameter is used. This is a real package vulnerability, though likely low severity unless the skill's code exposes attacker-controlled buffer usage through reachable API paths.

Static analysis

No suspicious patterns detected.