Clawd Casino

Security checks across malware telemetry and agentic risk

Overview

This casino skill is transparent about handling real USDC betting, but it asks for unusually powerful wallet authority and stores sensitive keys in ways users should review carefully.

Install only with a dedicated low-balance wallet created for this casino, never a wallet holding other assets. Avoid using --save unless you accept plaintext .env storage, keep .env out of version control and backups, prefer small explicit --amount approvals, approve only the game you need, and verify CASINO_API_URL before using the skill. Treat every roulette spin, PvP quote, accept, and approval command as a real USDC financial action.

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (32)

Tainted flow: 'API_URL' from os.getenv (line 25, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
return False

    # Get permit nonce and game address from unified API
    response = requests.get(
        f"{API_URL}/approve/{game_name}/permit-nonce", headers=get_api_key_header()
    )
    if response.status_code != 200:
Confidence
91% confidence
Finding
response = requests.get( f"{API_URL}/approve/{game_name}/permit-nonce", headers=get_api_key_header() )

Tainted flow: 'API_URL' from os.getenv (line 25, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
"s": "0x" + s.hex(),
    }

    response = requests.post(
        f"{API_URL}/approve/{game_name}",
        headers=get_wallet_signature_header(),
        json=permit_data,
Confidence
88% confidence
Finding
response = requests.post( f"{API_URL}/approve/{game_name}", headers=get_wallet_signature_header(), json=permit_data, )

Tainted flow: 'API_URL' from os.getenv (line 21, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
sys.exit(1)

    # Get permit nonce from API
    response = requests.get(f"{API_URL}/pvp/permit-nonce", headers=get_api_key_header())
    if response.status_code != 200:
        print("Error: Could not get permit nonce")
        return
Confidence
85% confidence
Finding
response = requests.get(f"{API_URL}/pvp/permit-nonce", headers=get_api_key_header())

Tainted flow: 'permit_data' from requests.get (line 427, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
}

    # /approve requires wallet signature (not API key) since we're proving wallet ownership for the permit
    response = requests.post(
        f"{API_URL}/pvp/approve",
        headers=get_wallet_signature_header(),
        json=permit_data,
Confidence
90% confidence
Finding
response = requests.post( f"{API_URL}/pvp/approve", headers=get_wallet_signature_header(), json=permit_data, )

Tainted flow: 'API_URL' from os.getenv (line 21, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}

    # /approve requires wallet signature (not API key) since we're proving wallet ownership for the permit
    response = requests.post(
        f"{API_URL}/pvp/approve",
        headers=get_wallet_signature_header(),
        json=permit_data,
Confidence
88% confidence
Finding
response = requests.post( f"{API_URL}/pvp/approve", headers=get_wallet_signature_header(), json=permit_data, )

Tainted flow: 'API_URL' from os.getenv (line 24, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
data["skill_version"] = skill_version

    print(f"Registering wallet: {wallet}")
    response = requests.post(
        f"{API_URL}/agent/register",
        headers=get_wallet_signature_header(),
        json=data,
Confidence
92% confidence
Finding
response = requests.post( f"{API_URL}/agent/register", headers=get_wallet_signature_header(), json=data, )

Tainted flow: 'API_URL' from os.getenv (line 18, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
if bet_value is not None:
        data["bet_value"] = bet_value

    response = requests.post(
        f"{API_URL}/roulette/spin", headers=get_api_key_header(), json=data
    )
    result = response.json()
Confidence
97% confidence
Finding
response = requests.post( f"{API_URL}/roulette/spin", headers=get_api_key_header(), json=data )

Tainted flow: 'API_URL' from os.getenv (line 18, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
def show_history(limit: int = 20):
    """Show roulette spin history."""
    response = requests.get(
        f"{API_URL}/roulette/history",
        headers=get_api_key_header(),
        params={"limit": limit},
Confidence
97% confidence
Finding
response = requests.get( f"{API_URL}/roulette/history", headers=get_api_key_header(), params={"limit": limit}, )

Tainted flow: 'API_URL' from os.getenv (line 18, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
def show_stat():
    """Show roulette statistics."""
    response = requests.get(f"{API_URL}/roulette/stat", headers=get_api_key_header())
    result = response.json()

    if result.get("success"):
Confidence
97% confidence
Finding
response = requests.get(f"{API_URL}/roulette/stat", headers=get_api_key_header())

Tainted flow: 'API_URL' from os.getenv (line 18, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
def show_balance():
    """Show USDC balance and approval status."""
    response = requests.get(f"{API_URL}/agent/me", headers=get_api_key_header())
    result = response.json()

    if result.get("success"):
Confidence
98% confidence
Finding
response = requests.get(f"{API_URL}/agent/me", headers=get_api_key_header())

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill clearly requires environment variables, file writes to `.env`, and network access to a remote casino API, yet no permissions are explicitly declared. This creates a transparency and governance gap: an operator may install or invoke the skill without understanding that it can handle secrets, write credentials locally, and interact with external services affecting funds.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The public description frames the skill as gameplay, but the documented behavior also includes generating wallets, handling private keys, retrieving API keys, storing credentials, and signing token approvals. That mismatch can mislead users about the sensitivity of the operations and increases the chance they will consent to risky actions without informed understanding.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill encourages `--save` flows that write a wallet private key and API key into `.env` automatically, but it does not prominently warn that these are highly sensitive secrets that may be readable by other local tools, users, logs, backups, or version control. Storing private keys in plaintext substantially raises the risk of credential theft and direct loss of funds.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The `/approve` flow authorizes USDC spending for all current games and explicitly mentions future games, but the wording emphasizes convenience rather than financial risk. Broad token approvals can expose user funds if the platform, approval scopes, or integrated game contracts are compromised or expanded unexpectedly.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The default approval amount is set to 1,000,000 USDC, creating a very large standing allowance with no explicit confirmation step. In a betting/casino skill, this is especially dangerous because users may treat approvals as routine gameplay setup while unknowingly granting excessive spending power that can be abused if the spender is compromised or malicious.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The code transmits wallet-authentication headers and the signed permit package to a remote service without any in-code user disclosure at the point of action. In this context, users are not just sending metadata—they are sending artifacts tied to token approval, so lack of transparency increases the chance of unsafe consent and misuse if the endpoint is untrusted or compromised.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The code defaults to approving 1,000,000 USDC without an interactive warning or confirmation. In a betting wallet context, such a large standing approval materially increases loss potential if the contract, relayer, API, or signing flow is compromised.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script promotes saving the API key into a plaintext .env file as the recommended workflow without warning about local secret exposure. Plaintext credential storage increases the chance of accidental disclosure through source control, backups, logs, shared workspaces, or permissive filesystem access.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The API key is printed directly to stdout after registration, which can expose it in terminal scrollback, shell history captures, CI logs, screen recordings, or multi-user environments. Since this is a live authentication credential, disclosure could allow unauthorized use of the agent account.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The spin command performs a real wager through an authenticated API call with no explicit confirmation, dry-run, or spend warning at execution time. In an agent skill context, that raises the risk of accidental or prompt-induced financial loss because a single command directly spends funds.

Missing User Warnings

High
Confidence
94% confidence
Finding
This function can sign an EIP-2612 USDC permit for an arbitrary spender and value using a hot private key from the environment, with no policy checks, allowlist, value limits, deadline bounds, or user confirmation. In the context of a casino skill handling USDC betting, that is especially dangerous because any upstream prompt injection, API abuse, or application logic flaw that reaches this function could generate a valid approval signature enabling token theft or draining of user-controlled funds associated with the wallet.

Credential Access

High
Category
Privilege Escalation
Content
/version                         Check if skill is up to date

      SETUP (one-time):
        /wallet-gen --save               Generate wallet → save to .env
        /register --name "MyAgent" --save   Register → save API key to .env
        /approve                         Approve USDC for ALL games (gasless)
Confidence
97% confidence
Finding
.env

Credential Access

High
Category
Privilege Escalation
Content
SETUP (one-time):
        /wallet-gen --save               Generate wallet → save to .env
        /register --name "MyAgent" --save   Register → save API key to .env
        /approve                         Approve USDC for ALL games (gasless)

      ACCOUNT:
Confidence
93% confidence
Finding
.env

Credential Access

High
Category
Privilege Escalation
Content
Recommended usage:
    /wallet-gen --save

The --save flag automatically saves your private key to .env
"""

import argparse
Confidence
96% confidence
Finding
.env

Credential Access

High
Category
Privilege Escalation
Content
def find_env_file() -> Path:
    """Find the .env file to save wallet key to."""
    candidates = [
        Path.cwd() / ".env",
        Path.cwd().parent / ".env",
Confidence
98% confidence
Finding
.env

VirusTotal

64/64 vendors flagged this skill as clean.

View on VirusTotal