Back to skill

Security audit

2026 02 10 Clawhub Base Wallet 1.5.0

Security checks for vulnerabilities and agentic risk

Overview

This wallet skill is mostly purpose-aligned, but it needs review because it handles private keys and performs high-impact signing and file operations with weak safeguards.

Install only if you are comfortable giving the skill access to wallet private keys and network services. Prefer test wallets, avoid funded wallets until the signing and path-validation issues are fixed, do not run BaseMail registration unless you intend to link that wallet to BaseMail, and treat stdout, .env files, managed wallet files, mnemonic backups, and audit logs as sensitive.

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/basemail-register.js:73
Finding
Unvalidated API-Controlled Message Signing<![CDATA[ ## Vulnerability Details **File Location**: `scripts/basemail-register.js:73-94` **Vulnerability Type**: Unrestricted signing of remote-controlled content **Risk Level**: High ### Vulnerable Code ```javascript const startRes = await fetch(`${API_BASE}/api/auth/start`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address: wallet.address }) }); const startData = await startRes.json(); if (!startData.message) { throw new Error('Failed to start auth: ' + JSON.stringify(startData)); } // Sign message const signature = await wallet.signMessage(startData.message); // Verify const verifyRes = await fetch(`${API_BASE}/api/auth/verify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: startData.message, signature, address: wallet.address }) }); return await verifyRes.json(); ``` ### Technical Analysis The script signs a message supplied entirely by `https://api.basemail.ai` without parsing or validating its contents. It does not verify that the message is a valid Sign-In with Ethereum statement containing the expected: - Domain: `basemail.ai` - URI: `https://basemail.ai` - Chain ID: `8453` - Wallet address - Nonce - Statement - Issuance and expiration times This differs from the protocol documented in `references/basemail-api.md`, which describes obtaining a nonce and constructing a fixed SIWE message. The implemented code instead treats any nonempty `message` value returned by `/api/auth/start` as safe to sign. Sending the public address, SIWE message, and resulting signature to BaseMail is necessary for the declared registration workflow. The private key and mnemonic are not transmitted. The vulnerability is that the remote service determines the unrestricted content to which the private key is applied, exceeding the minimum signing authority required for BaseMail authentication. ### Attack Path 1. A user invokes `basema ...[truncated 1349 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Retrieve only a nonce from the service and construct the complete SIWE message locally. 2. If the server must return the message, parse it with a maintained SIWE parser and reject it unless all security-sensitive fields match fixed expectations: - Domain exactly equals `basemail.ai`. - URI exactly equals `https://basemail.ai`. - Address exactly equals `wallet.address`, using canonical address comparison. - Chain ID exactly equals `8453`. - Nonce has the expected format and matches the nonce issued for the current flow. - Issuance time is recent. - Expiration time, if present, has not passed and is within a short allowed duration. - Statement and request ID are limited to explicitly approved values. 3. Reject malformed, duplicate, unexpected, or omitted SIWE fields. 4. Display the normalized message before signing. Require explicit confirmation if any nonstandard field is present. 5. Check `startRes.ok` and `verifyRes.ok` before processing response bodies. 6. Bind the nonce to one authentication attempt and prevent replay. 7. Keep the API origin fixed and do not permit environment variables or command-line arguments to override it without an explicit security warning. ]]>

T09 ยท Insecure Skill Coding Practices

Warning
Location
scripts/create-wallet.js:75
Finding
Wallet Name Path Traversal Allows Filesystem Access Outside the Wallet Directory<![CDATA[ ## Vulnerability Details **File Locations**: `scripts/create-wallet.js:75-79, 147-185`; `scripts/basemail-register.js:45-54, 101`; `scripts/check-balance.js:27-42` **Vulnerability Type**: Path traversal through an unsanitized wallet name **Risk Level**: Medium ### Vulnerable Code From `scripts/create-wallet.js`: ```javascript let walletName = 'default'; const managedIdx = args.indexOf('--managed'); if (managedIdx !== -1 && args[managedIdx + 1] && !args[managedIdx + 1].startsWith('-')) { walletName = args[managedIdx + 1]; } ``` ```javascript // Determine storage path const walletsDir = process.env.WALLET_DIR || path.join(process.env.HOME, '.openclaw', 'wallets'); fs.mkdirSync(walletsDir, { recursive: true, mode: 0o700 }); const filepath = path.join(walletsDir, `${walletName}.json`); // Check if exists if (fs.existsSync(filepath)) { const overwrite = await prompt(`\nโš ๏ธ Wallet "${walletName}" already exists. Overwrite? (yes/no): `); if (overwrite !== 'yes') { console.log('Cancelled.'); process.exit(0); } } // Save with restricted permissions fs.writeFileSync(filepath, JSON.stringify(walletData, null, 2), { mode: 0o600 }); // Also save mnemonic separately (read-only backup) const mnemonicPath = filepath.replace('.json', '.mnemonic'); fs.writeFileSync(mnemonicPath, wallet.mnemonic.phrase, { mode: 0o400 }); ``` From `scripts/basemail-register.js`: ```javascript const walletsDir = process.env.WALLET_DIR || path.join(process.env.HOME, '.openclaw', 'wallets'); const filepath = path.join(walletsDir, `${walletName}.json`); if (fs.existsSync(filepath)) { console.log(`๐Ÿ”‘ Using managed wallet: ${filepath}`); const data = JSON.parse(fs.readFileSync(filepath, 'utf8')); return new ethers.Wallet(data.privateKey); } ``` ```javascript const walletName = process.argv[2] || 'default'; ``` From `scripts/check-balance.js`: ```javascript const walletName = input || 'default'; const walletsDir = process.env.WALLET_DIR || path.joi ...[truncated 3710 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict wallet names to a conservative identifier format before any filesystem operation: ```javascript function validateWalletName(name) { if (!/^[A-Za-z0-9_-]{1,64}$/.test(name)) { throw new Error('Invalid wallet name'); } return name; } ``` 2. Resolve the base and destination paths and enforce containment: ```javascript const baseDir = path.resolve(walletsDir); const destination = path.resolve(baseDir, `${walletName}.json`); if ( destination === baseDir || !destination.startsWith(baseDir + path.sep) ) { throw new Error('Wallet path escapes the configured wallet directory'); } ``` 3. Apply the same validation helper consistently in all three scripts. 4. Reject names containing path separators, `..`, null bytes, control characters, or absolute paths. 5. Use exclusive creation such as `flag: 'wx'` for new wallet files to prevent accidental overwrites. 6. Check for symbolic links with `lstatSync()` and reject symlink destinations and unsafe parent-directory components. 7. Open files using descriptor-based operations where practical, verify the final file type, and avoid check-then-write patterns. 8. Validate loaded wallet JSON against a strict schema before using `privateKey` or `address`. 9. Avoid silently ignoring wallet-update failures, because this can conceal path and permission problems relevant to security. ]]>
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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill promises autonomous wallet creation, SIWE signing, and transaction sending, but the documented implementation also introduces managed/file storage, audit logging, and human-confirmation implications not reflected in the description. Misleading autonomy and capability claims are risky in security-sensitive wallet contexts because users may rely on assurances that do not match how secrets are stored or how actions are actually triggered.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill promises autonomous wallet creation, SIWE signing, and transaction sending, but the documented implementation also introduces managed/file storage, audit logging, and human-confirmation implications not reflected in the description. Misleading autonomy and capability claims are risky in security-sensitive wallet contexts because users may rely on assurances that do not match how secrets are stored or how actions are actually triggered.

Ae1

High
Category
analysis-evasion
Content
node scripts/create-wallet.js --env
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/create-wallet.js --env
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
PRIVATE_KEY="0x..." node scripts/basemail-register.js
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
PRIVATE_KEY="0x..." node scripts/basemail-register.js
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

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
The lockfile pins the skill to ws 8.17.1, which is identified as affected by memory disclosure and memory-exhaustion denial-of-service advisories. In a wallet skill for autonomous Web3 agents, websocket connectivity is commonly used for provider/event streams, so a vulnerable ws dependency increases risk of remote disruption or unintended memory exposure during network interaction.

Credential Access

High
Category
Privilege Escalation
Content
// Mode: --env (recommended)
  if (isEnv) {
    console.log('# ๐Ÿ” New Wallet Created');
    console.log('# Copy these lines to your shell or .env file:');
    console.log('');
    console.log(`export WALLET_ADDRESS="${wallet.address}"`);
    console.log(`export PRIVATE_KEY="${wallet.privateKey}"`);
Confidence
88% confidence
Finding
The script prints the raw private key and mnemonic to stdout and recommends copying the secret into shell or .env formats. In agent or automated environments, stdout is often logged, captured by orchestrators, stored in CI artifacts, or exposed to other components, making credential disclosure highly likely.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises capabilities that inherently require access to environment variables and network endpoints, but it does not declare any explicit tool scope or permission boundaries. In an agent setting, this can lead to overbroad execution assumptions, making it easier for the skill to access secrets or perform outbound actions without clear operator review.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
| โœ… DO | โŒ DON'T |
|-------|----------|
| Use **environment variables** for private keys | Store private keys in plain text files |
| Set wallet files to **chmod 600** | Commit wallet files to git |
| Use `--env` mode (recommended) | Use `console.log(privateKey)` |
| Back up mnemonics **offline** | Share private keys or mnemonics |
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
## Quick Start

### Create a New Wallet (Recommended)

```bash
# Output as environment variable format (safest)
Confidence
84% confidence
Finding
The recommended flow emits an export command containing the private key, encouraging long-lived shell/session persistence of a highly sensitive secret. Environment variables are often inherited by child processes, exposed through logs/history/process inspection in some environments, and can remain available longer than intended.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The transaction example shows how to send funds on Base mainnet but does not warn that blockchain transfers are irreversible, may incur fees, and can permanently move real assets if the recipient or amount is wrong. In an autonomous agent context, omission of confirmation and safety checks materially increases the chance of unintended fund loss.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This API reference exposes capabilities to send emails externally, read inbox contents, and trigger credit purchases tied to on-chain payments, but it provides no explicit warnings about privacy exposure, transmission to third parties, or the irreversibility of blockchain-funded actions. In the context of an autonomous wallet/email skill for AI agents, omission of these warnings increases the likelihood that an agent or integrator will perform sensitive or costly operations without adequate user confirmation or policy gating.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The code authenticates to and registers with an external email service using the wallet identity, which is a capability not clearly justified by the base-wallet description alone. In an autonomous agent setting, this can cause unintended identity linkage, third-party data disclosure, and account creation at an external service without sufficiently explicit user expectation or scope boundaries.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The script performs actions beyond simple wallet use: it registers an account with a third-party service and then mutates a local managed wallet file to persist BaseMail metadata. That expands the skill's effective permissions and creates side effects on local state that are not obviously implied by the stated wallet/signing/transaction scope, which is risky in an agent context because users may authorize wallet operations without expecting account creation and file modification.

Session Persistence

Medium
Category
Rogue Agent
Content
#!/usr/bin/env node
/**
 * Create a new Base/Ethereum wallet
 * 
 * Usage:
 *   node create-wallet.js                    # Show help
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
In managed mode, the script warns about saving the private key to a JSON file but also silently writes the mnemonic seed phrase to a second file. This materially increases secret exposure because the mnemonic can recreate the wallet and may be overlooked by users, backups, or security controls that only account for the JSON file.

Vague Triggers

Low
Confidence
83% confidence
Finding
This is a manifest file, so vague-trigger checks apply. The description and script names indicate wallet-management capabilities but do not specify how or when the skill should be invoked, nor do they provide narrow trigger phrases or exclusion conditions, which can lead to overly broad activation in agent ecosystems that rely on manifest metadata.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"balance": "node scripts/check-balance.js"
  },
  "dependencies": {
    "ethers": "^6.16.0"
  },
  "keywords": [
    "base",
Confidence
92% confidence
Finding
The dependency on ethers uses a caret range (^6.16.0), allowing automatic installation of newer compatible releases. In a wallet skill that can create wallets, sign messages, and send transactions, unreviewed dependency updates increase supply-chain risk: a compromised or breaking upstream release could alter cryptographic, signing, or transaction-handling behavior without explicit approval.

Missing User Warnings

Low
Confidence
83% confidence
Finding
This code accesses a wallet JSON file from WALLET_DIR or the user's home directory to resolve an address, but there is no prior user-facing warning or disclosure that local wallet files will be read. Although the read is non-destructive and aligned with the script's purpose, the file access is implicit rather than explicitly disclosed at the point of operation.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/basemail-register.js:19

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/create-wallet.js:118

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
SKILL.md:186