T03 · Remote Payload Retrieval and Execution
- Location
scripts/01_install_cast.sh:19- Finding
Unverified Remote Installer Is Piped Directly into Bash
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This wallet skill has a coherent purpose, but it handles crypto secrets in unsafe ways and makes broad local/system changes that need careful review before installation.
Install only after reviewing the scripts and understanding that this skill can handle complete wallet-control secrets. Do not use it with wallets holding meaningful funds; prefer a hardware wallet or separate signer, avoid importing existing seed phrases, decline unverified remote installers and sudo package changes, and delete ~/.agent-wallet plus any workspace transaction logs if you test it.
scripts/01_install_cast.sh:19Unverified Remote Installer Is Piped Directly into Bash
scripts/03_password.sh:19Encrypted Keystore and Plaintext Decryption Password Are Persisted Together
scripts/02_wallet.sh:180Mnemonic and Private Key Material Are Written to Plaintext Files
scripts/03_password.sh:45Wallet Secrets Are Exposed Through Process Arguments and Environment Variables
scripts/00_lib.sh:108Writable State Data Is Executed as Shell Code
scripts/02_wallet.sh:41Wallet Setup Attempts Privileged Installation of a System Scheduler
SKILL.md:28Skill Instructions Reduce Transparency Around Security-Sensitive Operations
The script downloads installer code directly from a remote URL and executes it locally without verifying a checksum, signature, or pinned version. If the remote endpoint, transport path, or hosting account is compromised, arbitrary code will run on the user's machine during installation.
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..."
Using curl ... | bash creates a direct command-execution chain from network input to the shell, eliminating any opportunity for integrity validation before execution. This makes compromise of the download source or a malicious response immediately equivalent to arbitrary code execution on the host.
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 script passes the raw private key as a command-line argument to 'cast wallet address --private-key', which can expose the secret through process listings, shell history wrappers, audit logs, or telemetry on multi-user systems. This directly contradicts the script's claim that the key will not be printed back and is especially dangerous because the file is explicitly handling highly sensitive wallet material.
The skill instructs the agent to read and write local files and run shell scripts, but it declares no explicit tool scope or permission boundaries. That creates an overbroad execution surface where wallet secrets, workspace files, and shell access can be used without an auditable least-privilege contract.
The skill expands beyond wallet operations into updating TOOLS.md, which is outside its stated functional scope. Scope expansion increases the chance of unintended workspace modification and can be abused to persist misleading or sensitive information in unrelated documentation.
The skill presents token metadata assets as authoritative reference data, then later instructs the agent to modify those same JSON assets during onboarding. Allowing runtime mutation of reference assets can poison future decisions, persist untrusted token data, and blur the line between trusted configuration and user-supplied input.
The skill explicitly persists a keystore, password stash, and network/token state across sessions as part of onboarding. Persisting wallet credentials and related state in agent-accessible local storage materially increases the blast radius of compromise, especially in an environment with shell and file-write capabilities.
## 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 instruction is written as a mandatory communication directive in Russian and tells the agent how to speak with the user, but it does not offer a language choice or indicate that the user opted into Russian. This is a natural-language locale policy issue because it imposes a specific language behavior without documented justification or consent.
The skill directs the agent to run readiness checks and reveal stored wallet status, keystore path, network details, and balances automatically each session without asking the user. Automatically surfacing sensitive local state increases the chance of unintended disclosure and normalizes agent-initiated actions on a high-risk wallet environment.
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.
The skill instructs the agent to collect highly sensitive key material, including mnemonics and private keys, and even save generated mnemonic words to a local file for an hour. In the context of an EVM wallet skill, this is especially dangerous because exposure of seed phrases or private keys enables complete and irreversible theft of funds across all derived accounts.
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 onboarding flow authorizes the agent to perform consequential wallet actions automatically once the absence of a wallet is detected, including key generation/import handling, password persistence, network configuration, and token-state changes. In a cryptocurrency context, autonomous progression around secret material and persistent wallet setup is especially dangerous because mistakes can lead to irreversible loss or unauthorized state changes.
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 requires persistent logging of wallet addresses, transaction hashes, and descriptions to a workspace file without user notice or consent. This creates a privacy and operational security risk because transaction history and wallet identifiers may be exposed to other tools, users, or future sessions.
Natural-language transaction logs can retain sensitive financial context, wallet identifiers, and operational intent in a durable workspace artifact. Because this data may be easier to search, copy, or leak than raw chain data, the logging requirement meaningfully increases disclosure risk.
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() {
This shell library writes and updates persistent state under the user's home directory via state_init, state_set, and state_unset, including creating ~/.agent-wallet/state.env and overwriting its contents. Although the operations are permission-hardened, there is no user-facing disclosure, prompt, or warning in this file indicating that persistent local state will be created and modified.
The add_custom_token_to_json function rewrites the target JSON file using json.dump(open(path, 'w'), ...), which can alter user-managed token configuration on disk. The function prints a success message after the write, but there is no warning or confirmation before performing the file modification.
The script expands its scope from wallet management into host package management by attempting to install 'at' with apt and optionally sudo. In a wallet-handling workflow, that broader system modification is risky because it can trigger privilege escalation prompts, alter the host unexpectedly, and increases the blast radius if the script or environment is compromised.
This line invokes sudo to run a shell command that installs a system package, giving the script a privileged execution path beyond its stated wallet-management purpose. Even if intended for convenience, privileged package installation from within a wallet script increases trust requirements and can be abused or cause unintended host changes.
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
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.