T03 · Remote Payload Retrieval and Execution
- Location
scripts/01_install_cast.sh:18- Finding
Unpinned Remote Installer Is Executed Directly by Bash
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This wallet skill matches its stated crypto purpose, but it handles wallet secrets and installation steps in ways that could put funds and the host system at risk.
Install only after reviewing the scripts and accepting the risk that the skill can create a local hot wallet, store wallet secrets and passwords on disk, modify local toolchain and package state, and reveal wallet metadata automatically. Use a low-value test wallet, avoid importing an existing funded seed phrase or private key, and prefer hardware or external signing for real funds.
scripts/01_install_cast.sh:18Unpinned Remote Installer Is Executed Directly by Bash
scripts/03_password.sh:19Keystore Password Is Persistently Stored in Plaintext
scripts/02_wallet.sh:180Mnemonic Phrase and Private Key Are Written to Plaintext Files
scripts/02_wallet.sh:43Wallet Setup Automatically Attempts Privileged Package Installation
scripts/00_lib.sh:107State File Is Executed as Shell Code
SKILL.md:8Skill Directs Persistent Modification of Agent Workspace Guidance
This finding reflects external script fetching that culminates in execution of remotely obtained code. Fetching and executing unpinned installer content from the internet introduces a supply-chain attack path; if the remote endpoint serves malicious content, the host can be fully compromised. Given this skill manages crypto wallet tooling, host compromise can directly lead to theft of private keys or tampering with transactions.
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..."
The script downloads code from a remote URL and immediately executes it via a shell pipeline. This creates a supply-chain and remote-code-execution risk: if the upstream site, transport, DNS, or install script is compromised, arbitrary commands will run on the user's machine with the user's privileges. In a wallet-oriented skill, compromise of the local environment is especially dangerous because it can expose keys, seed phrases, RPC credentials, and transaction workflows.
The use of a shell pipeline into bash is a classic unsafe chaining pattern because it removes opportunities to inspect or validate the downloaded content before execution. Any compromise of the source, network path, or redirection target immediately becomes arbitrary code execution. In the context of EVM wallet operations, that can translate into credential theft, malicious transaction substitution, or persistent backdoors on the operator's machine.
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 directs the agent to read, write, and execute shell scripts, but it declares no explicit tool scope or permissions boundary. In a wallet-management skill that handles secrets and filesystem state, this increases the chance that an agent can overreach into sensitive operations without clear user or platform constraints.
The skill establishes persistent local wallet state, including a keystore, password stash, and network metadata in a shared home-directory location. For a wallet-management skill, persistent storage of authentication material materially raises the risk of credential compromise, cross-session leakage, and unauthorized reuse by other tools or future prompts.
## 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 instructs the agent to run readiness checks automatically each session and immediately disclose saved wallet address, keystore path, balance, network name, and RPC URL without first asking the user. In the context of a crypto wallet, that is sensitive session persistence and can expose financial and filesystem details unexpectedly.
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 section instructs the agent to collect a mnemonic or private key from the user and, when generating a new wallet, save the mnemonic to a local file for later deletion. In a crypto wallet context, writing seed phrases or handling imported secrets through agent workflows is highly dangerous because any disclosure or filesystem leak can result in irreversible theft of funds.
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 normalizes autonomous handling of sensitive wallet setup steps, including saving mnemonic material temporarily, saving a password helper file, and auto-selecting RPC endpoints. In a wallet skill, reducing confirmations around these high-risk operations can lead to unsafe secret handling or unintended trust in infrastructure chosen by the skill rather than the user.
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 documentation says token additions are written directly into a shared JSON asset file, which blurs the line between trusted static metadata and user-provided runtime data. In a crypto context, mutating authoritative token metadata based on interactive input can poison future recommendations, validations, or contract selections.
The skill tells the agent to persist wallet address and transaction details without warning the user that their wallet activity will be stored locally. Because transaction history can reveal holdings and behavior patterns, silent persistence creates a privacy and data-handling risk beyond the user’s immediate request.
Keeping a running log of wallet addresses, transaction hashes, and reasons for mention creates a durable behavioral record tied to a financial identity. Even if no private keys are logged, this dataset can still be sensitive and can be abused for profiling, surveillance, or correlation with other local artifacts.
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 function derives a raw private key directly from a mnemonic and returns it to the caller, but there is no warning, consent gate, or indication that an extremely sensitive secret is being materialized in process memory and potentially surfaced to logs or downstream shell code. In a wallet-oriented skill, exposing a private key is especially risky because later code may persist, print, or mishandle it, turning seed phrase access into immediate key exfiltration risk.
Running foundryup modifies the user's local toolchain and environment, but the script does so without an explicit consent gate or clear explanation of what will change. While updater/install behavior is expected for a setup script, silent environment modification increases operational risk and can lead to unintended changes, especially on systems used for sensitive wallet operations.
The script goes beyond wallet operations and performs host package management by attempting to install the 'at' scheduler with apt/sudo. In an agent skill context, that expands privilege and system-modification scope unexpectedly, and if the skill runs unattended it could trigger privileged changes on the host without explicit user approval.
The script installs 'at' via apt/apt-get, potentially through sudo, without an upfront user-facing confirmation. Silent package installation is risky because it changes system state, may elevate privileges, and normalizes broad host access in a skill that users expect to only handle wallet setup.
This line invokes sudo to run a package installation command, which introduces privilege escalation into a wallet-creation script. In a crypto skill, mixing secret handling with root-capable host modification is especially dangerous because users may trust the workflow and approve privileged actions they did not expect.
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 writes mnemonic words and the raw private key to disk, including storing a recovery phrase for about an hour and a temporary private key file. Even with umask 077 and chmod 600, disk persistence materially increases the chance of secret theft from backups, other processes, crash dumps, agent state leakage, or delayed cleanup failures.
The script writes the keystore password to pw.txt in plaintext, then later uses that file to unlock the wallet. Anyone who gains filesystem access to the agent workspace, backups, logs, mounted volumes, or another process running as the same user can recover the password and decrypt the keystore, defeating most of the benefit of encrypting the private key at rest.
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.