Back to skill

Security audit

Virtuals

Security checks for vulnerabilities and agentic risk

Overview

This skill is not clearly malicious, but it asks users to paste and persist a crypto private key in plaintext while overstating trading/create features and security.

Review carefully before installing. Do not provide a real wallet private key to this skill; use a throwaway wallet only, and treat any key already entered through the documented command as potentially exposed through shell history and the plaintext config file. The market-data and balance features appear coherent, but the advertised trading/creation capability is not actually implemented in the inspected code.

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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
src/cli.ts:259
Finding
Unnecessary Collection and Plaintext Storage of Cryptocurrency Private Keys<![CDATA[ ## Vulnerability Details **File Location**: `src/cli.ts:29-31, 46-50, 225-230, 259-286`; documentation at `SKILL.md:64-67` **Vulnerability Type**: Sensitive credential exposure and violation of least privilege **Risk Level**: High ### Vulnerable Code ```ts interface Config { wallet?: string; privateKey?: string; } ``` ```ts function saveConfig(config: Config): void { ensureDir(); fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)); fs.chmodSync(CONFIG_FILE, 0o600); } ``` ```ts const config = loadConfig(); if (!config.privateKey) { console.log('❌ No wallet configured. Run:'); console.log(' virtuals config --wallet <address> --private-key <key>'); return; } ``` ```ts program .command('config') .description('Configure wallet for trading') .option('--wallet <address>', 'Wallet address') .option('--private-key <key>', 'Private key (stored securely)') .option('--show', 'Show current config') .action(async (options) => { if (options.show) { const config = loadConfig(); console.log('\n⚙️ Virtuals Configuration'); console.log('═══════════════════════════════════════'); console.log(` Wallet: ${config.wallet || 'Not set'}`); console.log(` Key: ${config.privateKey ? '••••••••' : 'Not set'}`); console.log('═══════════════════════════════════════'); return; } const config = loadConfig(); if (options.wallet) { config.wallet = options.wallet; } if (options.privateKey) { config.privateKey = options.privateKey; } saveConfig(config); console.log('✅ Configuration saved'); }); ``` The documented invocation also directs users to expose the key through a command-line argument: ```bash virtuals config --wallet <address> --private-key <key> ``` ### Technical Analysis The Skill requests a cryptocurrency private key as a command-line argument and persists it as unencrypted JSON in `~/.openclaw/virtuals/config.json`. ...[truncated 3057 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Remove private-key collection immediately** - Delete the `privateKey` configuration property and the `--private-key` option until transaction signing is actually implemented. - Remove the private-key presence check from `create`, because the command currently performs no transaction. - Remove existing documentation that instructs users to place private keys on the command line. 2. **Use external wallet signing** - Prefer a hardware wallet, browser wallet, WalletConnect-compatible provider, or other external signer. - Require the wallet to display and approve each transaction. - Do not give the Skill persistent access to raw private-key material. 3. **Avoid command-line secret arguments** - If local key import is unavoidable, collect it through a hidden interactive prompt or protected standard input. - Never include secrets in argv, logs, exceptions, telemetry, or shell examples. 4. **Use platform credential storage** - Store secrets in an operating-system credential vault rather than a plaintext JSON file. - Examples include macOS Keychain, Windows Credential Manager, or a Linux Secret Service implementation. - Encrypt secrets at rest and narrowly scope access to the required application identity. 5. **Require explicit transaction controls** - Display the chain ID, destination contract, function, token amounts, gas estimate, and maximum financial impact before signing. - Require affirmative user confirmation for every transaction. - Reject chain or contract mismatches. 6. **Correct the network documentation** - Reconcile the “testnet-only” statement with the Base Mainnet RPC and contract configuration. - Use explicit chain IDs and environment-specific contract allowlists. - Prevent mainnet signing when testnet mode is selected. 7. **Support secure cleanup and migration** - Warn existing users that `~/.openclaw/virtuals/config.json` may contain a plaintext private k ...[truncated 210 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (26)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This is a stronger form of the same issue: the skill claims trading and agent creation features while actually storing private keys locally, relying on undeclared resource access, and only redirecting users externally for core actions. Such deceptive or inaccurate documentation can lead to credential exposure and unsafe operational assumptions in a financial context.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This is a stronger form of the same issue: the skill claims trading and agent creation features while actually storing private keys locally, relying on undeclared resource access, and only redirecting users externally for core actions. Such deceptive or inaccurate documentation can lead to credential exposure and unsafe operational assumptions in a financial context.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The CLI accepts and persists a raw private key in a local JSON config file, even though the current code does not need signing functionality. Storing long-lived wallet secrets on disk greatly increases the chance of key theft through local compromise, backups, logs, malware, or accidental disclosure, and here the risk is especially unnecessary because no transaction flow is implemented.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The option text claims the private key is 'stored securely', but the implementation writes it directly to plaintext JSON on disk. This is dangerous because it gives users false confidence and may cause them to expose real wallet credentials under the mistaken belief that the application provides hardened secret handling.

Known Vulnerable Dependency: axios==1.13.5 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
97% confidence
Finding
The lockfile pins axios 1.13.5, which the scanner reports as affected by multiple advisories including SSRF/proxy-bypass and prototype-pollution-related request hijacking issues. In a skill that integrates with external protocols and likely performs network calls, a vulnerable HTTP client can enable request redirection, credential leakage, or access to internal resources if attacker-controlled URLs, redirects, or proxy settings are involved.

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
90% confidence
Finding
form-data 4.0.5 is reported as vulnerable to CRLF injection through unescaped multipart field names/filenames. If this skill ever builds multipart requests from user-controlled inputs, an attacker may be able to smuggle additional multipart headers or alter request structure, which can lead to request tampering or downstream parser confusion.

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
92% confidence
Finding
ws 8.17.1 is flagged for memory disclosure and memory-exhaustion denial-of-service issues. In a blockchain/agent-trading integration, WebSocket connections may be used for live events or provider streams, so malformed or hostile peer traffic could crash the process, degrade availability, or potentially expose process memory contents.

Known Vulnerable Dependency: axios==1.13.5 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The CLI claims the private key is 'stored securely', but saveConfig writes it as plaintext JSON to disk. Even with chmod 0600, malware, backup systems, local compromise, shell history exposure, or other processes running as the same user can recover the key and fully drain the wallet.

Lp3

Medium
Category
MCP Least Privilege
Confidence
81% confidence
Finding
The skill advertises capabilities that involve environment/config access but does not declare an explicit tool scope such as permissions or allowed-tools. In an agent ecosystem, missing scope declarations weaken user visibility and policy enforcement, making it easier for a skill to access sensitive local state or behave beyond what its manifest suggests.

Session Persistence

Medium
Category
Rogue Agent
Content
- 📊 **List agents** - Browse top AI agents on Virtuals
- 💰 **Check prices** - Get agent token prices and market data
- 🔍 **Agent details** - View agent info, holders, transactions
- 🚀 **Create agent** - Launch your own tokenized AI agent
- 💸 **Trade** - Buy/sell agent tokens

## Installation
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.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The documentation instructs users to pass a private key directly on the command line without explicit secure-handling guidance. Private keys entered this way may be exposed through shell history, process listings, logs, or insecure local config storage, which can directly lead to wallet compromise and fund loss.

External Transmission

Medium
Category
Data Exfiltration
Content
}
async function getVirtualPrice() {
    try {
        const response = await axios_1.default.get('https://api.coingecko.com/api/v3/simple/price?ids=virtual-protocol&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true');
        const data = response.data['virtual-protocol'];
        return {
            price: data.usd,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
}
async function getVirtualPrice() {
    try {
        const response = await axios_1.default.get('https://api.coingecko.com/api/v3/simple/price?ids=virtual-protocol&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true');
        const data = response.data['virtual-protocol'];
        return {
            price: data.usd,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
// For now, return placeholder
    try {
        // Try to get from their API if it exists
        const response = await axios_1.default.get('https://api.virtuals.io/agents?limit=10', {
            timeout: 5000,
        }).catch(() => null);
        if (response?.data) {
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
// For now, return placeholder
    try {
        // Try to get from their API if it exists
        const response = await axios_1.default.get('https://api.virtuals.io/agents?limit=10', {
            timeout: 5000,
        }).catch(() => null);
        if (response?.data) {
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest description presents active protocol integration for creating, managing, and trading agents on Base. In code, the `create` command only prints instructions to use an external website, while no commands perform onchain writes, agent management, or trading transactions; the implemented behavior is limited to price/balance queries and config storage.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The CLI accepts a private key without presenting a clear upfront warning that the secret will be stored locally and may be exposed if the host is compromised. In a crypto-related skill, users may paste production wallet keys, so missing safety prompts materially raises the risk of inadvertent credential exposure and financial loss.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest describes an integration that can create, manage, and trade tokenized AI agents on Base. In the code, the `create` command only checks for a stored private key and then tells the user to use an external website because smart contract integration is 'coming soon', and there are no trading or management commands at all.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The tool accepts a private key on the command line and persists it locally without any explicit warning about the sensitivity of that secret. In a crypto-wallet context this is especially dangerous because CLI arguments may be exposed via shell history, process listings, logs, or copied command transcripts before the key is even written to disk.

Known Vulnerable Dependency: follow-redirects==1.15.11 — 1 advisory(ies): CVE-2026-40895 (follow-redirects leaks Custom Authentication Headers to Cross-Domain Redirect Ta)

Low
Category
Supply Chain
Confidence
89% confidence
Finding
follow-redirects 1.15.11 is flagged for leaking custom authentication headers across cross-domain redirects. Because axios depends on this package, any authenticated HTTP request that follows attacker-influenced redirects could expose API keys, bearer tokens, or other sensitive headers to a different origin.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "IntechChain",
  "license": "MIT",
  "dependencies": {
    "commander": "^12.1.0",
    "ethers": "^6.9.0",
    "axios": "^1.6.0"
  },
Confidence
93% confidence
Finding
The dependency uses a caret range (^12.1.0), which allows npm to install newer compatible releases rather than a single fixed version. This weakens supply-chain determinism and can unexpectedly pull in a compromised or breaking release during future installs, though by itself it is a common package management practice rather than an immediately exploitable flaw.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "MIT",
  "dependencies": {
    "commander": "^12.1.0",
    "ethers": "^6.9.0",
    "axios": "^1.6.0"
  },
  "devDependencies": {
Confidence
93% confidence
Finding
The ethers dependency is specified with a caret range, so installs may resolve to newer package versions over time. In a blockchain-integrated skill that may handle wallets, transactions, or signing logic, non-deterministic dependency resolution increases supply-chain risk if an upstream release is malicious or unexpectedly insecure.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "commander": "^12.1.0",
    "ethers": "^6.9.0",
    "axios": "^1.6.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
Confidence
96% confidence
Finding
The axios dependency is version-ranged with a caret, allowing future installs to pull different releases. Because this skill integrates with external services and likely performs network requests, an unpinned HTTP client increases supply-chain exposure and can also make it easier to inherit vulnerable upstream versions without explicit review.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"axios": "^1.6.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}
Confidence
83% confidence
Finding
The @types/node devDependency is also unpinned, which affects build reproducibility. Although devDependencies are generally less dangerous at runtime, compromised or incompatible development tooling can still affect builds, generated artifacts, or developer environments.

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
dist/cli.js:49

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
src/cli.ts:13

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
dist/cli.js:270

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/cli.ts:271