T03 · Remote Payload Retrieval and Execution
- Location
scripts/01_install_cast.sh:14- Finding
Unverified Remote Installer Is Piped Directly Into a Shell
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This wallet skill is purpose-aligned, but it handles crypto secrets in ways that could expose funds and makes broad local/system changes.
Review carefully before installing. Do not use this with valuable wallets unless the secret-handling flow is changed: mnemonics, private keys, and keystore passwords should not be entered into chat or stored in plaintext files. Prefer a hardware wallet or external signer, verify any Foundry installation separately, and avoid allowing the skill to run sudo/package-manager commands or automatic balance checks unless you understand the exposure.
scripts/01_install_cast.sh:14Unverified Remote Installer Is Piped Directly Into a Shell
SKILL.md:29Skill Instructions Encourage Collection of Mnemonics and Private Keys Through the Agent
scripts/03_password.sh:25Keystore Password Is Always Retained in a Plaintext File
scripts/02_wallet.sh:179Mnemonic and Private Key Material Is Written to Plaintext Files
scripts/02_wallet.sh:43Wallet Setup Attempts Privileged Package Installation for Optional Cleanup
This section directs the agent to persist extremely sensitive wallet material and related metadata: saving generated mnemonic words to disk, storing the keystore password in a local helper file, keeping wallet state in persistent files, and logging transaction mentions. In a wallet-management context this is especially dangerous because compromise of these local artifacts can lead to wallet theft, long-term surveillance of financial activity, and recovery of funds by anyone obtaining the seed phrase or password file.
This finding is valid because the script fetches and executes an external installer from the network, creating a direct supply-chain execution path. Given the skill's purpose is managing EVM wallets and transactions, compromise of the installed tooling could lead to credential theft, transaction manipulation, or installation of backdoored binaries in the user's shell environment.
exit 0
fi
if ! has_cmd curl; then
err "curl is required but not found. Install curl and re-run."
exit 1
fi
warn "cast not found. Installing Foundry..."
Piping curl output directly into bash is a classic unsafe chaining pattern because it combines retrieval and execution into one step, leaving no opportunity for validation or integrity checks. In this wallet-oriented context, any attacker controlling the response could achieve arbitrary code execution and potentially persist in the user's shell startup files or exfiltrate wallet-related secrets.
exit 1
fi
warn "cast not found. Installing Foundry..."
curl -L https://foundry.paradigm.xyz | bash
enable_strict_path
if ! has_cmd foundryup; then
for p in "${HOME}/.bashrc" "${HOME}/.zshrc" "${HOME}/.profile"; do
The skill invokes shell scripts, reads local state, and writes sensitive wallet data, but declares no explicit tool scope or permission boundaries. In an agent environment, this increases the chance the skill is executed with broader-than-necessary filesystem and shell access, which is risky given it handles secrets and persistent state.
The onboarding scripts are designed to create or import keys, encrypt keystores, and persist network/token state across sessions. Persistence itself is expected for wallets, but the issue is that the skill normalizes long-lived local storage of highly sensitive state without clearly constraining where and how it is protected.
## Scripts
- **Step scripts** — `scripts/01_install_cast.sh`..`06_finish.sh` cover the onboarding flow described in the README: install Foundry/cast, create or import a key, encrypt the keystore, choose network/RPC/tokens (sourced from the JSON assets), and show the resulting address and balance. Run them in order when the user requests onboarding. Each script already prompts for the necessary inputs (mnemonic/private key, password, RPC URL, token details), so relaying the same questions to the user and then running the next script is the recommended approach.
- **Wallet health check** — `scripts/check_wallet.sh` inspects the shared state and reports whether a keystore/address pair already exists; it returns success (0) when a wallet is present and 1 otherwise.
- **Network status** — `scripts/show_network.sh` prints the active network name, chainId, and RPC URL from `~/.agent-wallet/state.env`, or warns if the configuration is incomplete.
- **Wallet removal** — `scripts/remove_wallet.sh` safely deletes the keystore, password stash, and metadata from `~/.agent-wallet/state.env` after an explicit confirmation.
The skill text describes token-step behavior that conflicts with the earlier script model, including direct JSON mutation of token metadata. In security-sensitive wallet workflows, inconsistent documentation can cause an agent to modify reference assets unexpectedly, potentially corrupting trusted network/token data or persisting user-provided values into shared configuration.
The skill instructs the agent to automatically run wallet readiness checks, reveal saved address and keystore path, and query balances/network status without first asking the user. This can expose sensitive local wallet metadata and trigger outbound RPC requests that reveal the user's address and IP-linked activity to third-party nodes.
Always ask the user, right before running each script, exactly the question that script itself will ask (password, network choice, etc.). Do not invent or fill in answers on their behalf—only use the information they explicitly provide. This keeps onboarding faithful to what they chose and avoids pushing the scripts forward with made-up data.
1. **Start with targeted help if stuck.** Pipe `cast --help` through `grep` (e.g., `cast --help | grep balance`) to zero in on the relevant subcommand and avoid scrolling the entire manual; this saves tokens and keeps the answer focused before you proceed or explain anything.
2. **Automatic readiness check.** Run `scripts/check_wallet.sh` automatically each session; do not ask the user to trigger it. If it detects an existing wallet, immediately display the saved address/keystore path and proceed to show the balance/network status (see next step) so the user sees “wallet ready” without extra probes.
3. **Show wallet + network status.** When `check_wallet` finds a wallet, run `scripts/show_network.sh` and query the balance (e.g., `cast balance <ADDRESS> --rpc-url <RPC_URL> --ether`) so the user sees the current native balance, network name, chainId, and RPC URL without being prompted to check anything manually.
4. **Onboarding flow** (automatic when no wallet exists). If the readiness check exits with 1, walk through the scripted steps in order, mirroring their prompts and explicitly asking the user for every required piece of information before running the next script. After the key-material step finishes, share the derived address immediately so the user sees it before we ask them for anything in step 3:
1. Installation — explain that the script will ensure Foundry/cast is installed so every mentioned `cast` command works before proceeding.
This instruction covers collecting mnemonics or private keys and persisting a generated mnemonic to a local file for later deletion. In a wallet skill, handling key material is inherently high risk, and writing seed phrases to disk substantially increases the chance of compromise from malware, backups, shell history, or other local users/processes.
3. **Show wallet + network status.** When `check_wallet` finds a wallet, run `scripts/show_network.sh` and query the balance (e.g., `cast balance <ADDRESS> --rpc-url <RPC_URL> --ether`) so the user sees the current native balance, network name, chainId, and RPC URL without being prompted to check anything manually.
4. **Onboarding flow** (automatic when no wallet exists). If the readiness check exits with 1, walk through the scripted steps in order, mirroring their prompts and explicitly asking the user for every required piece of information before running the next script. After the key-material step finishes, share the derived address immediately so the user sees it before we ask them for anything in step 3:
1. Installation — explain that the script will ensure Foundry/cast is installed so every mentioned `cast` command works before proceeding.
2. Key material — before running the wallet step, ask whether they want to create a new hot keypair, import a 12/24-word MetaMask-compatible mnemonic (`m/44'/60'/0'/0/0`), or import a private key. Collect the chosen secret, confirm the resulting address right after the step finishes, and tell the user that address before moving on. When generating a new keypair, capture the mnemonic displayed by `cast wallet new`, save it to `~/.agent-wallet/mnemonic-words-<timestamp>.txt`, and tell the user the exact path plus the fact that a job (via `at now + 1 hour` if available or a background `sleep` fallback) will delete that file after 60 minutes so the seed phrase does not linger.
3. Password — only ask for the keystore password once (there is no confirmation prompt, no save/remember question, and the account name is forced to “agent”). The script saves that password to the local helper file and uses it when creating the keystore, so nothing else is needed from the user for this step.
4. Network — read aloud the default network list derived from `assets/evm-networks.json`, ask which numbered network they want, and note
...[truncated 25 chars]
The skill authorizes automatic onboarding steps and post-step actions around wallet creation/import, address disclosure, network selection, and balance retrieval with limited confirmation boundaries. In a financial context, autonomous progression through sensitive setup can cause accidental secret handling, unintended persistence, or unwanted network interactions before the user has reviewed each action.
4. **Onboarding flow** (automatic when no wallet exists). If the readiness check exits with 1, walk through the scripted steps in order, mirroring their prompts and explicitly asking the user for every required piece of information before running the next script. After the key-material step finishes, share the derived address immediately so the user sees it before we ask them for anything in step 3:
1. Installation — explain that the script will ensure Foundry/cast is installed so every mentioned `cast` command works before proceeding.
2. Key material — before running the wallet step, ask whether they want to create a new hot keypair, import a 12/24-word MetaMask-compatible mnemonic (`m/44'/60'/0'/0/0`), or import a private key. Collect the chosen secret, confirm the resulting address right after the step finishes, and tell the user that address before moving on. When generating a new keypair, capture the mnemonic displayed by `cast wallet new`, save it to `~/.agent-wallet/mnemonic-words-<timestamp>.txt`, and tell the user the exact path plus the fact that a job (via `at now + 1 hour` if available or a background `sleep` fallback) will delete that file after 60 minutes so the seed phrase does not linger.
3. Password — only ask for the keystore password once (there is no confirmation prompt, no save/remember question, and the account name is forced to “agent”). The script saves that password to the local helper file and uses it when creating the keystore, so nothing else is needed from the user for this step.
4. Network — read aloud the default network list derived from `assets/evm-networks.json`, ask which numbered network they want, and note that the script now auto-selects the first RPC URL from that entry (it saves the matching `CHAIN_ID`/`ETH_RPC_URL` and then just shows the RPC so the user can see which endpoint is being used).
5. Tokens — the script now prints the token table derived from `assets/evm-network-tokens.json` so it appears directly i
...[truncated 25 chars]
The skill instructs the agent to write wallet addresses, transaction hashes, and descriptions to a local log without warning or consent. Even if not secret like a private key, this is sensitive financial metadata that can reveal identity, holdings, counterparties, and transaction patterns.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"name": "Avalanche C-Chain",
"chainId": 43114,
"rpcUrls": [
"https://api.avax.network/ext/bc/C/rpc"
],
"links": {
"docs": "https://build.avax.network/docs/primary-network",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"name": "Avalanche C-Chain",
"chainId": 43114,
"rpcUrls": [
"https://api.avax.network/ext/bc/C/rpc"
],
"links": {
"docs": "https://build.avax.network/docs/primary-network",
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
ensure_app_dir() {
mkdir -p "${APP_DIR}"
chmod 700 "${APP_DIR}" || true
}
state_init() {
The function derives a raw private key directly from a mnemonic phrase and returns it to the caller without any warning, consent gate, or minimization of secret exposure. In a wallet-management skill, this is especially sensitive because it normalizes handling the most dangerous credential material in plaintext, increasing the chance of accidental logging, persistence, shell history leakage, or downstream misuse.
The script downloads code from a remote URL and immediately pipes it to bash, which executes whatever the server returns at runtime with no integrity verification, review step, or confirmation prompt. In a wallet-management skill, this is especially dangerous because a compromised upstream host, TLS interception, or malicious update could install trojaned tooling that steals private keys, tampers with transactions, or alters RPC/network behavior.
The script expands its scope from wallet management into host package management by attempting to install at via apt/sudo. In a wallet-handling context, invoking privileged system changes increases trust requirements and attack surface, and could surprise users or be abused if the execution environment or package sources are compromised.
The script invokes sudo sh -c to install a package, which causes privileged shell execution from a wallet onboarding flow. Even though the immediate command is fixed, any unnecessary privilege escalation in a secret-handling skill materially raises risk if the script, environment, or package supply chain is tampered with.
return 1
fi
if [[ -x "/usr/bin/sudo" ]]; then
if ! sudo sh -c "$installer > /tmp/at-install.log 2>&1"; then
warn "Failed to install 'at' with sudo; mnemonic cleanup will be manual."
return 1
fi
The script handles highly sensitive wallet secrets and writes both the mnemonic and private key to plaintext files on disk, even if permissions are restricted. In a wallet-management skill, this is especially dangerous because any local compromise, backup sync, crash dump, or later file exposure can directly lead to irreversible theft of funds.
The script writes the wallet keystore password directly to a plaintext file at ${APP_DIR}/pw.txt, then later uses that file to unlock the keystore. Although file permissions are restricted with umask 077 and chmod 600, any local compromise of the user account, backups, logs, sync tools, or accidental file disclosure exposes the password and defeats much of the benefit of encrypting the keystore.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
state_set "SAVE_PASSWORD" "y"
umask 077
printf "%s" "$PW" > "${PASSWORD_FILE}"
chmod 600 "${PASSWORD_FILE}" || true
ok "Password saved to ${PASSWORD_FILE}"
acct_name="agent"
state_set "KEYSTORE_NAME" "${acct_name}"
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
state_set "SAVE_PASSWORD" "y"
umask 077
printf "%s" "$PW" > "${PASSWORD_FILE}"
chmod 600 "${PASSWORD_FILE}" || true
ok "Password saved to ${PASSWORD_FILE}"
acct_name="agent"
state_set "KEYSTORE_NAME" "${acct_name}"
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
state_set "SAVE_PASSWORD" "y"
umask 077
printf "%s" "$PW" > "${PASSWORD_FILE}"
chmod 600 "${PASSWORD_FILE}" || true
ok "Password saved to ${PASSWORD_FILE}"
acct_name="agent"
state_set "KEYSTORE_NAME" "${acct_name}"
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
state_set "SAVE_PASSWORD" "y"
umask 077
printf "%s" "$PW" > "${PASSWORD_FILE}"
chmod 600 "${PASSWORD_FILE}" || true
ok "Password saved to ${PASSWORD_FILE}"
acct_name="agent"
state_set "KEYSTORE_NAME" "${acct_name}"
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
state_set "SAVE_PASSWORD" "y"
umask 077
printf "%s" "$PW" > "${PASSWORD_FILE}"
chmod 600 "${PASSWORD_FILE}" || true
ok "Password saved to ${PASSWORD_FILE}"
acct_name="agent"
state_set "KEYSTORE_NAME" "${acct_name}"
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
state_set "SAVE_PASSWORD" "y"
umask 077
printf "%s" "$PW" > "${PASSWORD_FILE}"
chmod 600 "${PASSWORD_FILE}" || true
ok "Password saved to ${PASSWORD_FILE}"
acct_name="agent"
state_set "KEYSTORE_NAME" "${acct_name}"
No suspicious patterns detected.